Министерство науки и высшего образования Российской Федерации
Федеральное государственное автономное образовательное учреждение
высшего образования
ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ
УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР)
Кафедра комплексной информационной безопасности электронно-вычислительных систем (КИБЭВС)
БИНАРНЫЕ ДЕРЕВЬЯ ПОИСКА
Отчёт по практической работе №5
по дисциплине «Структуры данных»
Студент гр. 714-1
_______ И.С. Максимов
__.__.2025
Доцент кафедры КИБЭВС
_______ Е.Е. Лунёва
__.__.2025
Оглавление
Введение 3
Заключение 10
Введение
Целью данной работы является реализация бинарного дерева поиска с использованием динамических структур.
По отчету: недочет, нет вводного абзаца, с описание структуры классов. Рисунки нумерация не по ОС ТУСУР (сквозная, а нужно с привязкой к номеру раздела)
Ход работы
AddItem: добавляет элемент в дерево. Если дерево пустое, элемент становится корнем, иначе вызывается рекурсивная вставка (рис. 1)
Рисунок 1 — Метод AddItem
AddRecursive: рекурсивно находит место для нового элемента, перемещаясь влево или вправо по дереву, в зависимости от значения элемента (рис. 2)
Рисунок 2 — Метод AddRecursive
RemoveItem: удаляет элемент из дерева. Вызывает рекурсивную функцию для поиска и удаления узла с заданным значением (рис. 3)
Рисунок 3 — Метод RemoveItem
RemoveRecursive: рекурсивно ищет элемент для удаления, перестраивая дерево в зависимости от наличия потомков у удаляемого узла (рис. 4)
Рисунок 4 — Метод RemoveRecursive
Clear: очищает дерево, удаляя все узлы, сбрасывая корень дерева (рис. 5)
Рисунок 5 — Метод Clear
Print: выводит элементы дерева в ширину, начиная с корневого узла и проходя по уровням дерева (рис. 6)
Рисунок 6 — Метод Print
FindMax: находит и возвращает максимальный элемент дерева, проходя по правым потомкам до конца (рис. 7)
Рисунок 7 — Метод FindMax
FindMinNode: находит и возвращает минимальный элемент дерева. Для этого проходит по левым потомкам, начиная от корня, до самого крайнего левого кеузла, который и является минимальным элементом (рис. 8)
Рисунок 8 – Метод FindMinNode
Текстовое описание реализации удаления элемента из дерева: функция удаления элемента в бинарном дереве начинается с поиска узла, который нужно удалить. Если узел найден, есть несколько вариантов. Если узел — это лист (у него нет потомков), его просто удаляют. Если у узла есть один потомок (левый или правый), узел заменяется этим потомком. Если у узла два потомка, находят наименьший элемент в правом поддереве (наименьший элемент больше удаляемого узла), копируют его значение в узел, который нужно удалить, а затем удаляют этот минимальный узел. После выполнения этих действий дерево остаётся упорядоченным (рис. 9,10,11)
Рисунок 9 – Удаление листа
Рисунок 10 – Удаление при одном поддереве
Рисунок 11 – Удаление при двух поддеревьях
Заключение
В ходе работы было создано бинарное дерево поиска с поддержкой основных операций: добавление, удаление, очистка, вывод, поиск максимального элементов.
Приложение а
Листинг кода: using System;
// Класс узла бинарного дерева
class TreeNode
{
public int Data; // Значение узла
public TreeNode Left; // Левый потомок
public TreeNode Right; // Правый потомок
// Конструктор инициализирует узел с заданным значением, дети пусты
public TreeNode(int data)
{
Data = data;
Left = null;
Right = null;
}
}
// Класс узла для собственной очереди обхода дерева
class QueueNode
{
public TreeNode Data; // Данные - узел дерева
public QueueNode Next; // Следующий узел в очереди
public QueueNode(TreeNode data)
{
Data = data;
Next = null;
}
}
// Простая очередь, реализованная как связный список для обхода дерева
class SimpleQueue
{
private QueueNode head; // Голова очереди - первый элемент
private QueueNode tail; // Хвост очереди - последний элемент
// Конструктор создает пустую очередь
public SimpleQueue()
{
head = null;
tail = null;
}
// Добавляет узел в конец очереди
public void Enqueue(TreeNode data)
{
QueueNode newNode = new QueueNode(data);
if (tail == null) // Если очередь пуста, голова и хвост совпадают
{
head = tail = newNode;
}
else // Иначе вставляем в хвост и обновляем ссылку
{
tail.Next = newNode;
tail = newNode;
}
}
// Удаляет и возвращает узел из головы очереди, если нет элементов - возвращает null
public TreeNode Dequeue()
{
if (head == null) return null;
TreeNode data = head.Data;
head = head.Next;
if (head == null)
tail = null;
return data;
}
// Проверяет, пуста ли очередь
public bool IsEmpty()
{
return head == null;
}
}
// Класс бинарного дерева поиска с основными методами
class BinarySearchTree
{
private TreeNode root; // Корень дерева
// Конструктор инициализирует пустое дерево
public BinarySearchTree()
{
root = null;
}
// Добавляет элемент в дерево, возвращает true если добавлен успешно (нет дубликата)
public bool AddItem(int a)
{
if (root == null) // Если дерево пустое, создаём корень
{
root = new TreeNode(a);
return true;
}
return AddRecursive(root, a); // Иначе рекурсивно добавляем
}
// Рекурсивный метод добавления, ищет место для нового элемента
private bool AddRecursive(TreeNode node, int a)
{
if (a == node.Data)
return false; // Элемент уже есть, не добавляем дубли
if (a < node.Data) // Идём в левое поддерево
{
if (node.Left == null)
{
node.Left = new TreeNode(a);
return true;
}
else
return AddRecursive(node.Left, a);
}
else // Идём в правое поддерево
{
if (node.Right == null)
{
node.Right = new TreeNode(a);
return true;
}
else
return AddRecursive(node.Right, a);
}
}
// Удаляет элемент из дерева, возвращает true, если удаление произошло
public bool RemoveItem(int a)
{
bool removed;
root = RemoveRecursive(root, a, out removed);
return removed;
}
// Рекурсивное удаление элемента с поддержанием свойств дерева
private TreeNode RemoveRecursive(TreeNode node, int a, out bool removed)
{
removed = false;
if (node == null)
return null; // Элемент не найден
if (a < node.Data) // Идём в левое поддерево
node.Left = RemoveRecursive(node.Left, a, out removed);
else if (a > node.Data) // Идём в правое поддерево
node.Right = RemoveRecursive(node.Right, a, out removed);
else
{
removed = true; // Элемент найден, удаляем
if (node.Left == null) // Если нет левого ребенка, возвращаем правого
return node.Right;
else if (node.Right == null) // Если нет правого ребенка, возвращаем левого
return node.Left;
// Если есть оба ребенка:
// Находим минимальный элемент в правом поддереве (наименьший потомок)
TreeNode minRight = FindMinNode(node.Right);
node.Data = minRight.Data; // Заменяем значение удаляемого узла
// Удаляем дубликат значения из правого поддерева
node.Right = RemoveRecursive(node.Right, minRight.Data, out _);
}
return node;
}
// Находит узел с минимальным значением в поддереве, идя влево
private TreeNode FindMinNode(TreeNode node)
{
while (node.Left != null)
node = node.Left;
return node;
}
// Очищает дерево (корень становится null)
public void Clear()
{
root = null;
}
// Выводит дерево в консоль методом поиска в ширину (по уровням)
// Вывод дерева в консоль - поиск в ширину (уровень за уровнем)
public void Print()
{
if (root == null) // Если дерево пустое, сообщаем об этом
{
Console.WriteLine("(empty)");
return;
}
SimpleQueue queue = new SimpleQueue(); // Создаем пустую очередь для обхода
queue.Enqueue(root); // Помещаем корень дерева в очередь
while (!queue.IsEmpty()) // Пока в очереди есть элементы
{
TreeNode current = queue.Dequeue(); // Извлекаем элемент из начала очереди
Console.Write(current.Data + " "); // Выводим его значение на экран
// Добавляем детей текущего элемента в очередь, если они существуют
if (current.Left != null)
queue.Enqueue(current.Left);
if (current.Right != null)
queue.Enqueue(current.Right);
}
Console.WriteLine(); // Переход на новую строку после вывода всех элементов
}
// Поиск максимального элемента в дереве
public int? FindMax()
{
if (root == null) // Если дерево пустое, возвращаем null
return null;
TreeNode current = root;
// Проходим по правым потомкам, так как в бинарном дереве поиска справа содержатся большие значения
while (current.Right != null)
current = current.Right;
// Возвращаем значение самого правого узла - максимального
return current.Data;
}
// Класс с меню для взаимодействия с пользователем
class Program
{
static void Main()
{
BinarySearchTree bst = new BinarySearchTree();
while (true)
{
Console.WriteLine("\nМеню:");
Console.WriteLine("1. Добавить элемент");
Console.WriteLine("2. Удалить элемент");
Console.WriteLine("3. Очистить дерево");
Console.WriteLine("4. Показать дерево (поиск в ширину)");
Console.WriteLine("5. Найти максимальный элемент");
Console.WriteLine("0. Выход");
Console.Write("Ваш выбор: ");
string input = Console.ReadLine();
if (!int.TryParse(input, out int choice))
{
Console.WriteLine("Ошибка ввода.");
continue;
}
if (choice == 0)
break;
switch (choice)
{
case 1:
Console.Write("Введите элемент для добавления: ");
if (int.TryParse(Console.ReadLine(), out int addVal))
{
bool added = bst.AddItem(addVal);
Console.WriteLine(added ? "Элемент добавлен." : "Элемент уже существует.");
}
else Console.WriteLine("Ошибка ввода.");
break;
case 2:
Console.Write("Введите элемент для удаления: ");
if (int.TryParse(Console.ReadLine(), out int remVal))
{
bool removed = bst.RemoveItem(remVal);
Console.WriteLine(removed ? "Элемент удалён." : "Элемент не найден.");
}
else Console.WriteLine("Ошибка ввода.");
break;
case 3:
bst.Clear();
Console.WriteLine("Дерево очищено.");
break;
case 4:
Console.WriteLine("Дерево (поиск в ширину):");
bst.Print();
break;
case 5:
int? maxVal = bst.FindMax();
if (maxVal.HasValue)
Console.WriteLine($"Максимальный элемент: {maxVal.Value}");
else Console.WriteLine("Дерево пустое.");
break;
default:
Console.WriteLine("Недопустимый выбор.");
break;
}
}
Console.WriteLine("Выход из программы.");
}
}
}
Томск 2025
