Бинарное дерево поиска (Двоичное Search Дерево (сокращенно BST) — это специальное двоичное дерево, обладающее следующими свойствами: для каждого узла дерева значения всех узлов его левого поддерева меньше значения его узла, а значения всех узлов в его правом поддереве меньше значения его узла. Все значения больше значения его узла. Эта структура данных очень распространена в информатике, поскольку она обеспечивает эффективный способ хранения и извлечения данных. В этой статье мы подробно рассмотрим алгоритмы реализации бинарных деревьев поиска в C#, включая создание дерева、вставлять、удалить、Такие операции, как поиск и перемещение.
Прежде чем подробно обсуждать алгоритм, давайте сначала определим некоторые основные понятия двоичных деревьев поиска:
Чтобы реализовать двоичное дерево поиска в C#, нам сначала нужно определить класс узла, а затем реализовать основные операции с деревом.
public class TreeNode
{
public int Value { get; set; }
public TreeNode Left { get; set; }
public TreeNode Right { get; set; }
public TreeNode(int value)
{
Value = value;
Left = null;
Right = null;
}
}
Создание бинарного дерева поиска обычно начинается с пустого дерева, а затем вставляются узлы один за другим.
public class BinarySearchTree
{
public TreeNode Root { get; set; }
public BinarySearchTree()
{
Root = null;
}
}
Операция вставки является ядром двоичного дерева поиска, которое должно поддерживать поисковую природу дерева. Временная сложность операции вставки равна O(h), где h — высота дерева.
public void Insert(int value)
{
Root = Insert(Root, value);
}
private TreeNode Insert(TreeNode node, int value)
{
if (node == null)
{
node = new TreeNode(value);
}
else if (value < node.Value)
{
node.Left = Insert(node.Left, value);
}
else if (value > node.Value)
{
node.Right = Insert(node.Right, value);
}
return node;
}
Операция поиска проверяет, существует ли значение в двоичном дереве поиска. Временная сложность операции поиска также равна O(h).
public bool Search(int value)
{
return Search(Root, value) != null;
}
private TreeNode Search(TreeNode node, int value)
{
if (node == null || node.Value == value)
{
return node;
}
if (value < node.Value)
{
return Search(node.Left, value);
}
return Search(node.Right, value);
}
Операция удаления является более сложной операцией в бинарном дереве поиска. Необходимо учитывать три ситуации: удаление конечных узлов, удаление узлов только с одним дочерним узлом и удаление узлов с двумя дочерними узлами.
public void Delete(int value)
{
Root = Delete(Root, value);
}
private TreeNode Delete(TreeNode node, int value)
{
if (node == null)
{
return node;
}
if (value < node.Value)
{
node.Left = Delete(node.Left, value);
}
else if (value > node.Value)
{
node.Right = Delete(node.Right, value);
}
else
{
if (node.Left == null)
{
return node.Right;
}
else if (node.Right == null)
{
return node.Left;
}
node.Value = FindMinValue(node.Right);
node.Right = Delete(node.Right, node.Value);
}
return node;
}
private int FindMinValue(TreeNode node)
{
while (node.Left != null)
{
node = node.Left;
}
return node.Value;
}
Операция обхода заключается в посещении каждого узла дерева. Общие методы обхода включают обход в предварительном порядке, обход по порядку, обход после порядка и обход по уровню.
public void InOrderTraversal(Action<int> action)
{
InOrderTraversal(Root, action);
}
private void InOrderTraversal(TreeNode node, Action<int> action)
{
if (node != null)
{
InOrderTraversal(node.Left, action);
action(node.Value);
InOrderTraversal(node.Right, action);
}
}
Хотя деревья двоичного поиска обеспечивают эффективные операции с данными, в худшем случае (например, когда дерево сильно несбалансировано) его производительность снижается до O(n). Для оптимизации производительности мы можем рассмотреть следующие стратегии: