From aa6f33feb585eefe6c49c54aec67a9c0db53bb2e Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Wed, 31 Jan 2024 18:55:18 +0100 Subject: [PATCH] algorithms(avl): add changes Signed-off-by: Matej Focko --- .../files/algorithms/rb-trees/avl/src/AVL.cs | 82 ++++++++++++++++++- .../files/algorithms/rb-trees/avl/src/Node.cs | 14 +++- .../files/algorithms/rb-trees/avl/test/AVL.cs | 68 ++++++++++++++- .../algorithms/rb-trees/avl/test/Node.cs | 8 -- 4 files changed, 154 insertions(+), 18 deletions(-) delete mode 100644 static/files/algorithms/rb-trees/avl/test/Node.cs diff --git a/static/files/algorithms/rb-trees/avl/src/AVL.cs b/static/files/algorithms/rb-trees/avl/src/AVL.cs index f8210ad..757d8d4 100644 --- a/static/files/algorithms/rb-trees/avl/src/AVL.cs +++ b/static/files/algorithms/rb-trees/avl/src/AVL.cs @@ -1,6 +1,6 @@ using System.Collections; -namespace avl; +namespace AVL; class AVL(IComparer comparator) : IEnumerable { private Node? _root = null; @@ -10,11 +10,87 @@ class AVL(IComparer comparator) : IEnumerable { public int Count { get; private set; } = 0; public IComparer Comparator { get; } = comparator; + #region AVLSpecific + + private static (bool correct, int depth) Check(Node? node, int depth) { + if (node == null) { + return (true, depth); + } + + var (leftCorrect, leftDepth) = Check(node.Left, 1 + depth); + if (!leftCorrect) { + return (false, leftDepth); + } + + var (rightCorrect, rightDepth) = Check(node.Right, 1 + depth); + + var foundDepth = Math.Max(leftDepth, rightDepth); + return (rightCorrect && Math.Abs(leftDepth - rightDepth) <= 1, foundDepth); + } + + public bool IsCorrect() { + var (correct, _) = Check(_root, 0); + return correct; + } + + private void InsertRebalance(List> nodes) { + // TODO + } + + private void DeleteRebalance(List> nodes) { + // TODO + } + + #endregion + public bool Add(T item) { - throw new NotImplementedException(); + int cmp; + + // insert the node + var path = new List>(); + var node = _root; + while (node != null) { + path.Add(node); + cmp = Comparator.Compare(node.Value, item); + + if (cmp < 0) { + node = node.Right; + } else if (cmp == 0) { + return false; + } else { + node = node.Left; + } + } + + var newItem = new Node(item); + ++Count; + + // adding root + if (path.Count == 0) { + _root = newItem; + return true; + } + + var lastIdx = path.Count - 1; + cmp = Comparator.Compare(path[lastIdx].Value, item); + + if (cmp < 0) { + path[lastIdx].Right = newItem; + } else { + path[lastIdx].Left = newItem; + } + path.Add(newItem); + + // rebalance + InsertRebalance(path); + return true; } public bool Remove(T item) { + // delete the node + + // rebalance + throw new NotImplementedException(); } @@ -46,4 +122,4 @@ class AVL(IComparer comparator) : IEnumerable { } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); -} +} diff --git a/static/files/algorithms/rb-trees/avl/src/Node.cs b/static/files/algorithms/rb-trees/avl/src/Node.cs index b9e2096..fbf969b 100644 --- a/static/files/algorithms/rb-trees/avl/src/Node.cs +++ b/static/files/algorithms/rb-trees/avl/src/Node.cs @@ -1,12 +1,18 @@ -namespace avl; +namespace AVL; + +enum NodeType { + Minus, + Zero, + Plus, +} class Node { public T Value { get; init; } - public readonly Node? Parent; - public readonly Node? Left, Right; + public Node? Left = null, Right = null; + public NodeType Type = NodeType.Zero; public Node(T value) { Value = value; } -} +} diff --git a/static/files/algorithms/rb-trees/avl/test/AVL.cs b/static/files/algorithms/rb-trees/avl/test/AVL.cs index 85cc2e8..a8ea078 100644 --- a/static/files/algorithms/rb-trees/avl/test/AVL.cs +++ b/static/files/algorithms/rb-trees/avl/test/AVL.cs @@ -1,4 +1,4 @@ -namespace avl.test; +namespace AVL.Test; public class AVL { [Fact] @@ -8,8 +8,10 @@ public class AVL { Assert.Equal(0, t.Count); for (int i = 0; i < 10; ++i) { - Assert.False(t.Contains(i)); + Assert.False(t.Contains(i), $"Empty tree should not contain {i}"); } + + Assert.True(t.IsCorrect(), "AVL invariants hold for an empty tree"); } [Fact] @@ -19,6 +21,66 @@ public class AVL { }; Assert.Equal(1, t.Count); - Assert.True(t.Contains(1)); + Assert.True(t.Contains(123), "Tree contains the added element 123"); + + Assert.True(t.IsCorrect(), "AVL invariants hold"); + } + + [Fact] + public void TwoNodes() { + var t = new AVL { + 1, 2 + }; + + Assert.Equal(2, t.Count); + Assert.True(t.Contains(1), "Tree contains the added element 1"); + Assert.True(t.Contains(2), "Tree contains the added element 2"); + + Assert.True(t.IsCorrect(), "AVL invariants hold"); + } + + [Fact] + public void TwoNodesReversed() { + var t = new AVL { + 2, 1 + }; + + Assert.Equal(2, t.Count); + Assert.True(t.Contains(1), "Tree contains the added element 1"); + Assert.True(t.Contains(2), "Tree contains the added element 2"); + + Assert.True(t.IsCorrect(), "AVL invariants hold"); + } + + [Fact] + public void ThreeNodes() { + var t = new AVL { + 1, 2, 3 + }; + + Assert.Equal(3, t.Count); + Assert.True(t.Contains(1), "Tree contains the added element 1"); + Assert.True(t.Contains(2), "Tree contains the added element 2"); + Assert.True(t.Contains(3), "Tree contains the added element 3"); + + Assert.True(t.IsCorrect(), "AVL invariants hold"); + } + + + [Fact] + public void BiggerTree() { + var t = new AVL(); + + for (int i = -10; i <= 10; ++i) { + t.Add(i); + } + + Assert.Equal(21, t.Count); + + for (int i = -100; i <= 100; ++i) { + Assert.Equal(-10 <= i && i <= 10, t.Contains(i)); + } + + Assert.True(t.IsCorrect(), "AVL invariants hold"); } } diff --git a/static/files/algorithms/rb-trees/avl/test/Node.cs b/static/files/algorithms/rb-trees/avl/test/Node.cs deleted file mode 100644 index abba823..0000000 --- a/static/files/algorithms/rb-trees/avl/test/Node.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace avl.test; - -public class Node { - [Fact] - public void Test1() { - - } -}