diff --git a/Chapter5_BreadthFirstSearch/Program.cs b/Chapter5_BreadthFirstSearch/Program.cs index 667ee4b..fb9c2b5 100644 --- a/Chapter5_BreadthFirstSearch/Program.cs +++ b/Chapter5_BreadthFirstSearch/Program.cs @@ -1,5 +1,8 @@ using GrokAlgorithms.Chapter5_BreadthFirstSearch; +// Тут используется реализация алгоритма поиска в ширину на графе, BFS. +// Поиск в глубину (DFS) точно такой же, но вместо очереди использует стек + int count = 0; // Команда для построения дерева diff --git a/Chapter8_BalancedTree/AvlTree.cs b/Chapter8_BalancedTree/AvlTree.cs new file mode 100644 index 0000000..94e840e --- /dev/null +++ b/Chapter8_BalancedTree/AvlTree.cs @@ -0,0 +1,205 @@ +using System.Collections; + +class AvlTree : IEnumerable where T : notnull +{ + private IComparer _comparer; + private TreeNode? _root; + + public AvlTree(IComparer 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(); + 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 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 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 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 TraverseDfs() + { + return Dfs(_root); + } + + public IEnumerator 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); + } + } +} \ No newline at end of file diff --git a/Chapter8_BalancedTree/Chapter8_BalancedTree.csproj b/Chapter8_BalancedTree/Chapter8_BalancedTree.csproj new file mode 100644 index 0000000..206b89a --- /dev/null +++ b/Chapter8_BalancedTree/Chapter8_BalancedTree.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/Chapter8_BalancedTree/Program.cs b/Chapter8_BalancedTree/Program.cs new file mode 100644 index 0000000..5408ae1 --- /dev/null +++ b/Chapter8_BalancedTree/Program.cs @@ -0,0 +1,29 @@ +var avl = new AvlTree(Comparer.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)}"); \ No newline at end of file diff --git a/GrokAlgorithms.sln b/GrokAlgorithms.sln index b09cb54..74fefe8 100644 --- a/GrokAlgorithms.sln +++ b/GrokAlgorithms.sln @@ -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