[Глава 8] Сбалансированные деревья: АВЛ-дерево
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
using GrokAlgorithms.Chapter5_BreadthFirstSearch;
|
using GrokAlgorithms.Chapter5_BreadthFirstSearch;
|
||||||
|
|
||||||
|
// Тут используется реализация алгоритма поиска в ширину на графе, BFS.
|
||||||
|
// Поиск в глубину (DFS) точно такой же, но вместо очереди использует стек
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
// Команда для построения дерева
|
// Команда для построения дерева
|
||||||
|
|||||||
205
Chapter8_BalancedTree/AvlTree.cs
Normal file
205
Chapter8_BalancedTree/AvlTree.cs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
class AvlTree<T> : IEnumerable<T> where T : notnull
|
||||||
|
{
|
||||||
|
private IComparer<T> _comparer;
|
||||||
|
private TreeNode? _root;
|
||||||
|
|
||||||
|
public AvlTree(IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
_comparer = comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(T value)
|
||||||
|
{
|
||||||
|
_root = Insert(_root, value, _comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(T value)
|
||||||
|
{
|
||||||
|
_root = Remove(_root, value, _comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count(T value)
|
||||||
|
{
|
||||||
|
var curNode = _root;
|
||||||
|
|
||||||
|
while (curNode != null)
|
||||||
|
{
|
||||||
|
var cmpResult = _comparer.Compare(value, curNode.Value);
|
||||||
|
if (cmpResult == 0)
|
||||||
|
return curNode.Count;
|
||||||
|
else if (cmpResult > 0)
|
||||||
|
curNode = curNode.Right;
|
||||||
|
else if (cmpResult < 0)
|
||||||
|
curNode = curNode.Left;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return PrintTree(_root, "", true);
|
||||||
|
|
||||||
|
string PrintTree(TreeNode? node, string indent, bool last)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var result = indent;
|
||||||
|
result += last ? "└─" : "├─";
|
||||||
|
result += $"{node.Value} (h={node.Height}, c={node.Count})\n";
|
||||||
|
|
||||||
|
indent += last ? " " : "│ ";
|
||||||
|
|
||||||
|
var children = new List<TreeNode>();
|
||||||
|
if (node.Right != null) children.Add(node.Right);
|
||||||
|
if (node.Left != null) children.Add(node.Left);
|
||||||
|
|
||||||
|
for (int i = 0; i < children.Count; i++)
|
||||||
|
{
|
||||||
|
result += PrintTree(children[i], indent, i == children.Count - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode Insert(TreeNode? node, T value, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return new TreeNode() { Value = value };
|
||||||
|
var cmpResult = comparer.Compare(value, node.Value);
|
||||||
|
if (cmpResult < 0)
|
||||||
|
node.Left = Insert(node.Left, value, comparer);
|
||||||
|
else if (cmpResult > 0)
|
||||||
|
node.Right = Insert(node.Right, value, comparer);
|
||||||
|
else
|
||||||
|
node.Count++;
|
||||||
|
return Balance(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode? Remove(TreeNode? node, T value, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var cmpResult = comparer.Compare(value, node.Value);
|
||||||
|
if (cmpResult != 0)
|
||||||
|
{
|
||||||
|
if (cmpResult > 0)
|
||||||
|
node.Right = Remove(node.Right, value, comparer);
|
||||||
|
else if (cmpResult < 0)
|
||||||
|
node.Left = Remove(node.Left, value, comparer);
|
||||||
|
return Balance(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Count--;
|
||||||
|
if (node.Count > 0) // Если после уменьшения счётчика ещё есть элементы
|
||||||
|
return node;
|
||||||
|
|
||||||
|
|
||||||
|
if (node.Right == null) // Простой случай, когда нет правого узла - возвращаем левый
|
||||||
|
return node.Left;
|
||||||
|
|
||||||
|
TreeNode minRight;
|
||||||
|
var newRight = RemoveMin(node.Right, out minRight);
|
||||||
|
minRight.Left = node.Left;
|
||||||
|
minRight.Right = newRight;
|
||||||
|
return Balance(minRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode? RemoveMin(TreeNode node, out TreeNode minNode)
|
||||||
|
{
|
||||||
|
if (node.Left == null)
|
||||||
|
{
|
||||||
|
minNode = node;
|
||||||
|
return node.Right;
|
||||||
|
}
|
||||||
|
node.Left = RemoveMin(node.Left, out minNode);
|
||||||
|
return Balance(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode RotateRight(TreeNode p)
|
||||||
|
{
|
||||||
|
var q = p.Left!;
|
||||||
|
p.Left = q.Right;
|
||||||
|
q.Right = p;
|
||||||
|
p.UpdateHeight();
|
||||||
|
q.UpdateHeight();
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode RotateLeft(TreeNode p)
|
||||||
|
{
|
||||||
|
var q = p.Right!;
|
||||||
|
p.Right = q.Left;
|
||||||
|
q.Left = p;
|
||||||
|
p.UpdateHeight();
|
||||||
|
q.UpdateHeight();
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeNode Balance(TreeNode node)
|
||||||
|
{
|
||||||
|
node.UpdateHeight();
|
||||||
|
if (node.BFactor > 1)
|
||||||
|
{
|
||||||
|
if (node.Right?.BFactor < 0)
|
||||||
|
node.Right = RotateRight(node.Right);
|
||||||
|
return RotateLeft(node);
|
||||||
|
}
|
||||||
|
else if (node.BFactor < -1)
|
||||||
|
{
|
||||||
|
if (node.Left?.BFactor > 0)
|
||||||
|
node.Left = RotateLeft(node.Left);
|
||||||
|
return RotateRight(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<T> Dfs(TreeNode? node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var val in Dfs(node.Left))
|
||||||
|
yield return val;
|
||||||
|
|
||||||
|
for (int i = 0; i < node.Count; i++)
|
||||||
|
yield return node.Value;
|
||||||
|
|
||||||
|
foreach (var val in Dfs(node.Right))
|
||||||
|
yield return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<T> TraverseDfs()
|
||||||
|
{
|
||||||
|
return Dfs(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
return TraverseDfs().GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return TraverseDfs().GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TreeNode
|
||||||
|
{
|
||||||
|
public required T Value;
|
||||||
|
public TreeNode? Left;
|
||||||
|
public TreeNode? Right;
|
||||||
|
public int Height { get; private set; } = 1;
|
||||||
|
public int BFactor { get; private set; } = 0;
|
||||||
|
public int Count = 1;
|
||||||
|
|
||||||
|
public void UpdateHeight()
|
||||||
|
{
|
||||||
|
Height = Math.Max(Left?.Height ?? 0, Right?.Height ?? 0) + 1;
|
||||||
|
BFactor = (Right?.Height ?? 0) - (Left?.Height ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Chapter8_BalancedTree/Chapter8_BalancedTree.csproj
Normal file
10
Chapter8_BalancedTree/Chapter8_BalancedTree.csproj
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
29
Chapter8_BalancedTree/Program.cs
Normal file
29
Chapter8_BalancedTree/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
var avl = new AvlTree<int>(Comparer<int>.Default);
|
||||||
|
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(10);
|
||||||
|
avl.Insert(20);
|
||||||
|
avl.Insert(5);
|
||||||
|
avl.Insert(15);
|
||||||
|
avl.Insert(25);
|
||||||
|
avl.Insert(2);
|
||||||
|
avl.Insert(8);
|
||||||
|
avl.Insert(8);
|
||||||
|
avl.Insert(8);
|
||||||
|
|
||||||
|
System.Console.WriteLine(avl);
|
||||||
|
|
||||||
|
System.Console.WriteLine("Удаление");
|
||||||
|
avl.Remove(10);
|
||||||
|
|
||||||
|
System.Console.WriteLine(avl);
|
||||||
|
|
||||||
|
foreach (var v in avl)
|
||||||
|
System.Console.WriteLine($"{v} ");
|
||||||
|
|
||||||
|
foreach (var v in new[] { 8, 10, 100 })
|
||||||
|
System.Console.WriteLine($"Avl {v}: count={avl.Count(v)}");
|
||||||
@@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter4_QuickSort", "Chapt
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter5_BreadthFirstSearch", "Chapter5_BreadthFirstSearch\Chapter5_BreadthFirstSearch.csproj", "{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter5_BreadthFirstSearch", "Chapter5_BreadthFirstSearch\Chapter5_BreadthFirstSearch.csproj", "{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter8_BalancedTree", "Chapter8_BalancedTree\Chapter8_BalancedTree.csproj", "{4344D4B1-E64B-4C4C-A137-923A210BCD20}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -20,5 +22,9 @@ Global
|
|||||||
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4344D4B1-E64B-4C4C-A137-923A210BCD20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4344D4B1-E64B-4C4C-A137-923A210BCD20}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4344D4B1-E64B-4C4C-A137-923A210BCD20}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4344D4B1-E64B-4C4C-A137-923A210BCD20}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user