[Глава 8] Сбалансированные деревья: АВЛ-дерево
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
using GrokAlgorithms.Chapter5_BreadthFirstSearch;
|
||||
|
||||
// Тут используется реализация алгоритма поиска в ширину на графе, BFS.
|
||||
// Поиск в глубину (DFS) точно такой же, но вместо очереди использует стек
|
||||
|
||||
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,19 +6,25 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter4_QuickSort", "Chapt
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter5_BreadthFirstSearch", "Chapter5_BreadthFirstSearch\Chapter5_BreadthFirstSearch.csproj", "{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter8_BalancedTree", "Chapter8_BalancedTree\Chapter8_BalancedTree.csproj", "{4344D4B1-E64B-4C4C-A137-923A210BCD20}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Debug|Any CPU.ActiveCfg = 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.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4088F633-80E4-44DF-9DE4-996A4B3E3088}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F54FC00B-FB3D-4A8B-ABA8-33A43FC74DE4}.Debug|Any CPU.ActiveCfg = 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.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
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user