From 59e39e9cabf80ad70e5b88f9b9843b735ef68bf1 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Fri, 6 Dec 2024 21:59:34 +0600 Subject: [PATCH 1/7] Add insert implementation with illustrations --- Algorithms/DataStructures/AVLTree.fs | 233 +++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 Algorithms/DataStructures/AVLTree.fs diff --git a/Algorithms/DataStructures/AVLTree.fs b/Algorithms/DataStructures/AVLTree.fs new file mode 100644 index 0000000..6a1c12e --- /dev/null +++ b/Algorithms/DataStructures/AVLTree.fs @@ -0,0 +1,233 @@ +namespace Algorithms.DataStructures + +module AVLTree = + + type AVLNode = { + Value: int + Height: int + LeftChild: Option + RightChild: Option + } + + module AVLNode = + let create (value: int) : AVLNode = + { + Value = value + Height = 0 + LeftChild = None + RightChild = None + } + + let height (maybeNode: Option) : int = + maybeNode + |> Option.map (fun node -> node.Height) + |> Option.defaultValue -1 + + let balanceFactor (node: AVLNode) : int = + height node.RightChild - height node.LeftChild + + type AVLNode + with + member this.UpdateLeftChild (leftChild: Option) : AVLNode = + { + this with + LeftChild = leftChild + Height = 1 + max (AVLNode.height leftChild) (AVLNode.height this.RightChild) + } + member this.UpdateRightChild (rightChild: Option) : AVLNode = + { + this with + RightChild = rightChild + Height = 1 + max (AVLNode.height this.LeftChild) (AVLNode.height rightChild) + } + + module AVLTree = + let rotateRight (node: AVLNode) : AVLNode = + // Left child becomes the new root + let leftChild = node.LeftChild |> Option.get + let oldNode = node.UpdateLeftChild (leftChild.RightChild) + leftChild.UpdateRightChild (Some oldNode) + + let rotateLeft (node: AVLNode) : AVLNode = + // Right child becomes the new root + let rightChild = node.RightChild |> Option.get + let oldNode = node.UpdateRightChild (rightChild.LeftChild) + rightChild.UpdateLeftChild (Some oldNode) + + + type AVLTree = { + Root: Option + } + + let insert (value: int) (tree: AVLTree) : AVLTree = + let rec insertImpl (maybeNode: Option) : AVLNode = + match maybeNode with + | None -> + AVLNode.create value + | Some node -> + let node = + if value < node.Value then + node.UpdateLeftChild (Some (insertImpl node.LeftChild)) + elif value = node.Value then + node + else + node.UpdateRightChild (Some (insertImpl node.RightChild)) + + if AVLNode.balanceFactor node > 1 then + // Root node is right heavy, current balance factor is 2 + // check the balance factor of the right child + if node.RightChild |> Option.get |> AVLNode.balanceFactor < 0 then + // Right child is left heavy + // rotate right around right child and rotate left around root + + // Illustration: possible heights are shown in brackets, + // and the balance factor of the node is shown in parentheses + + // Initial state: + // + // b (+2) [h+3] + // / \ + // [h] a f (-1) [h+2] + // /\ + // [h+1] (0|-1|+1) d g [h] + // /\ + // [h|h-1] c e [h|h-1] + + // rotate right around f (right child) + // + // b (+2) [h+3] + // / \ + // [h] a d (+1|+2) [h+2] + // /\ + // [h|h-1] c f (0|-1) [h+1] + // /\ + // [h|h-1] e g [h] + + // rotate left around b (root) + // d (0) [h+2] + // __________/\__________ + // / \ + // [h+1] (0|-1) b f (0|-1) [h+1] + // / \ /\ + // [h] a c [h|h-1] [h|h-1] e g [h] + + + let node =node.UpdateRightChild (Some (AVLTree.rotateRight (node.RightChild |> Option.get))) + AVLTree.rotateLeft node + else + // Right child is balanced or left heavy, + // rotate left around root + + // Illustration if right child is balanced + + // Initial state: + // b (+2) [h+3] + // / \ + // [h] a d (0) [h+2] + // /\ + // [h+1] c e [h+1] + + // rotate left around b (root) + // d (-1) [h+3] + // / \ + // [h+2] (+1) b e [h+1] + // / \ + // [h] a c [h+1] + + // Illustration if right child is right heavy + + // Initial state: + // b (+2) [h+3] + // / \ + // [h] a d (+1) [h+2] + // /\ + // [h] c e [h+1] + + // rotate left around b (root) + // d (0) [h+2] + // / \ + // [h+1] (0) b e [h+1] + // / \ + // [h] a c [h] + + AVLTree.rotateLeft node + elif AVLNode.balanceFactor node < -1 then + // Root node is left heavy, current balance factor is -2 + // check the balance factor of the left child + if node.LeftChild |> Option.get |> AVLNode.balanceFactor > 0 then + // Left child is right heavy + // rotate left around left child and rotate right around root + + // Initial state: + // f (-2) [h+3] + // / \ + // [h+2] (+1) b g [h] + // /\ + // [h] a d (0|-1|+1) [h+1] + // /\ + // [h|h-1] c e [h|h-1] + + // rotate left around b (left child) + // f (-2) [h+3] + // / \ + // [h+2] (-2) d g [h] + // / \ + // [h+1] b e [h|h-1] + // /\ + // [h] a c [h|h-1] + + // rotate right around f (root) + // d (0) [h+2] + // __________/\__________ + // / \ + // [h+1] (0|-1) b f (0|-1) [h+1] + // / \ /\ + // [h] a c [h|h-1] [h|h-1] e g [h] + + let node = node.UpdateLeftChild (Some (AVLTree.rotateLeft (node.LeftChild |> Option.get))) + AVLTree.rotateRight node + else + // Left child is balanced or left heavy + // rotate right around root + + // Illustration if left child is balanced + + // Initial state: + // d (-2) [h+3] + // / \ + // [h+2] (0) b e [h] + // / \ + // [h+1] a c [h+1] + + // rotate right around d (root) + // b (+1) [h+3] + // / \ + // [h+1] a d (-1) [h+2] + // / \ + // [h+1]c e [h] + + // Illustration if left child is left heavy + + // Initial state: + // d (-2) [h+3] + // / \ + // [h+2] (-1) b e [h] + // / \ + // [h+1] a c [h] + + // rotate right around d (root) + // b (0) [h+2] + // / \ + // [h+1] a d (0) [h+1] + // / \ + // [h] c e [h] + + AVLTree.rotateRight node + else + // Balance of root is within acceptable range + node + + + insertImpl tree.Root + |> fun root -> { Root = Some root } + From c76bdb53df04bc885ae8ff348ad9f6b6b37a8789 Mon Sep 17 00:00:00 2001 From: mahdihasnat Date: Fri, 6 Dec 2024 15:59:50 +0000 Subject: [PATCH 2/7] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index e354294..d333615 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -41,6 +41,7 @@ ## Algorithms * Datastructures + * [Avltree](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/DataStructures/AVLTree.fs) * [Treap](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/DataStructures/Treap.fs) * [Trie](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/DataStructures/Trie.fs) * Math From 8269cbadcf5956ea2b4fa6ab8135b534448152a6 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Fri, 6 Dec 2024 22:19:08 +0600 Subject: [PATCH 3/7] Add tests for insert --- Algorithms.Tests/DataStructures/AVLTree.fs | 173 +++++++++++++++++++++ Algorithms/DataStructures/AVLTree.fs | 2 + 2 files changed, 175 insertions(+) create mode 100644 Algorithms.Tests/DataStructures/AVLTree.fs diff --git a/Algorithms.Tests/DataStructures/AVLTree.fs b/Algorithms.Tests/DataStructures/AVLTree.fs new file mode 100644 index 0000000..7be2ba9 --- /dev/null +++ b/Algorithms.Tests/DataStructures/AVLTree.fs @@ -0,0 +1,173 @@ +namespace Algorithms.Tests.DataStructures + +open Microsoft.VisualStudio.TestTools.UnitTesting +open Algorithms.DataStructures.AVLTree + +[] +type AVLTreeTests() = + + let rec verifyBalance (maybeNode: Option) = + match maybeNode with + | None -> () + | Some node -> + let bf = AVLNode.balanceFactor node + Assert.IsTrue(bf >= -1 && bf <= 1, $"Balance factor {bf} is out of range") + verifyBalance node.LeftChild + verifyBalance node.RightChild + + let rec verifyHeights (maybeNode: Option) = + match maybeNode with + | None -> () + | Some node -> + let leftHeight = AVLNode.height node.LeftChild + let rightHeight = AVLNode.height node.RightChild + Assert.AreEqual(node.Height, 1 + max leftHeight rightHeight) + + verifyHeights node.LeftChild + verifyHeights node.RightChild + + let rec verifyBST (maybeNode: Option) : Option< (* Min *) int * (* Max*) int> = + match maybeNode with + | None -> None + | Some node -> + let maybeLeftMinMax = verifyBST node.LeftChild + let maybeRightMinMax = verifyBST node.RightChild + maybeLeftMinMax + |> Option.iter (fun (_, leftMax) -> + Assert.IsTrue(leftMax < node.Value, $"Left child {leftMax} is greater than parent {node.Value}") + ) + maybeRightMinMax + |> Option.iter (fun (rightMin, _) -> + Assert.IsTrue(rightMin > node.Value, $"Right child {rightMin} is less than parent {node.Value}") + ) + let minValue = + maybeLeftMinMax + |> Option.map fst + |> Option.defaultValue node.Value + let maxValue = + maybeRightMinMax + |> Option.map snd + |> Option.defaultValue node.Value + Some (minValue, maxValue) + + let verifyProperties (tree: AVLTree) = + verifyBalance tree.Root + verifyHeights tree.Root + verifyBST tree.Root |> ignore + tree + + [] + member _.``Empty tree has no root``() = + let tree = + empty + |> verifyProperties + + Assert.IsTrue(tree.Root.IsNone) + + [] + member _.``Insert maintains AVL properties``() = + let tree = + empty + |> verifyProperties + |> insert 5 + |> verifyProperties + |> insert 3 + |> verifyProperties + |> insert 7 + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 9 + |> verifyProperties + |> insert 1 + |> verifyProperties + () + + [] + member _.``Right-heavy case triggers rotation``() = + let tree = + empty + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 2 + |> verifyProperties + |> insert 3 + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + let root = tree.Root.Value + Assert.AreEqual(2, root.Value) + Assert.AreEqual(Some 1, root.LeftChild |> Option.map (fun n -> n.Value)) + Assert.AreEqual(Some 3, root.RightChild |> Option.map (fun n -> n.Value)) + + [] + member _.``Left-heavy case triggers rotation``() = + let tree = + empty + |> verifyProperties + |> insert 3 + |> verifyProperties + |> insert 2 + |> verifyProperties + |> insert 1 + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + let root = tree.Root.Value + Assert.AreEqual(2, root.Value) + Assert.AreEqual(Some 1, root.LeftChild |> Option.map (fun n -> n.Value)) + Assert.AreEqual(Some 3, root.RightChild |> Option.map (fun n -> n.Value)) + + [] + member _.``Double rotation for right-left case``() = + let tree = + empty + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 3 + |> verifyProperties + |> insert 2 + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + let root = tree.Root.Value + Assert.AreEqual(2, root.Value) + Assert.AreEqual(Some 1, root.LeftChild |> Option.map (fun n -> n.Value)) + Assert.AreEqual(Some 3, root.RightChild |> Option.map (fun n -> n.Value)) + + [] + member _.``Double rotation for left-right case``() = + let tree = + empty + |> verifyProperties + |> insert 3 + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 2 + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + let root = tree.Root.Value + Assert.AreEqual(2, root.Value) + Assert.AreEqual(Some 1, root.LeftChild |> Option.map (fun n -> n.Value)) + Assert.AreEqual(Some 3, root.RightChild |> Option.map (fun n -> n.Value)) + + [] + member _.``Duplicate insert is idempotent``() = + let tree1 = + empty + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 2 + |> verifyProperties + + let tree2 = + insert 2 tree1 + |> verifyProperties + + Assert.AreEqual(tree1.Root |> Option.map (fun n -> n.Value), + tree2.Root |> Option.map (fun n -> n.Value)) \ No newline at end of file diff --git a/Algorithms/DataStructures/AVLTree.fs b/Algorithms/DataStructures/AVLTree.fs index 6a1c12e..b13dd80 100644 --- a/Algorithms/DataStructures/AVLTree.fs +++ b/Algorithms/DataStructures/AVLTree.fs @@ -59,6 +59,8 @@ module AVLTree = Root: Option } + let empty = { Root = None } + let insert (value: int) (tree: AVLTree) : AVLTree = let rec insertImpl (maybeNode: Option) : AVLNode = match maybeNode with From 0f61b35e130cba55dfd0eb13c77d020243161ef3 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Fri, 6 Dec 2024 22:42:28 +0600 Subject: [PATCH 4/7] implement delete --- Algorithms/DataStructures/AVLTree.fs | 366 +++++++++++++++------------ 1 file changed, 206 insertions(+), 160 deletions(-) diff --git a/Algorithms/DataStructures/AVLTree.fs b/Algorithms/DataStructures/AVLTree.fs index b13dd80..599db32 100644 --- a/Algorithms/DataStructures/AVLTree.fs +++ b/Algorithms/DataStructures/AVLTree.fs @@ -61,175 +61,221 @@ module AVLTree = let empty = { Root = None } + let private rebalance (node: AVLNode) : AVLNode = + if AVLNode.balanceFactor node > 1 then + // Root node is right heavy, current balance factor is 2 + // check the balance factor of the right child + if node.RightChild |> Option.get |> AVLNode.balanceFactor < 0 then + // Right child is left heavy + // rotate right around right child and rotate left around root + + // Illustration: possible heights are shown in brackets, + // and the balance factor of the node is shown in parentheses + + // Initial state: + // + // b (+2) [h+3] + // / \ + // [h] a f (-1) [h+2] + // /\ + // [h+1] (0|-1|+1) d g [h] + // /\ + // [h|h-1] c e [h|h-1] + + // rotate right around f (right child) + // + // b (+2) [h+3] + // / \ + // [h] a d (+1|+2) [h+2] + // /\ + // [h|h-1] c f (0|-1) [h+1] + // /\ + // [h|h-1] e g [h] + + // rotate left around b (root) + // d (0) [h+2] + // __________/\__________ + // / \ + // [h+1] (0|-1) b f (0|-1) [h+1] + // / \ /\ + // [h] a c [h|h-1] [h|h-1] e g [h] + + + let node =node.UpdateRightChild (Some (AVLTree.rotateRight (node.RightChild |> Option.get))) + AVLTree.rotateLeft node + else + // Right child is balanced or left heavy, + // rotate left around root + + // Illustration if right child is balanced + + // Initial state: + // b (+2) [h+3] + // / \ + // [h] a d (0) [h+2] + // /\ + // [h+1] c e [h+1] + + // rotate left around b (root) + // d (-1) [h+3] + // / \ + // [h+2] (+1) b e [h+1] + // / \ + // [h] a c [h+1] + + // Illustration if right child is right heavy + + // Initial state: + // b (+2) [h+3] + // / \ + // [h] a d (+1) [h+2] + // /\ + // [h] c e [h+1] + + // rotate left around b (root) + // d (0) [h+2] + // / \ + // [h+1] (0) b e [h+1] + // / \ + // [h] a c [h] + + AVLTree.rotateLeft node + elif AVLNode.balanceFactor node < -1 then + // Root node is left heavy, current balance factor is -2 + // check the balance factor of the left child + if node.LeftChild |> Option.get |> AVLNode.balanceFactor > 0 then + // Left child is right heavy + // rotate left around left child and rotate right around root + + // Initial state: + // f (-2) [h+3] + // / \ + // [h+2] (+1) b g [h] + // /\ + // [h] a d (0|-1|+1) [h+1] + // /\ + // [h|h-1] c e [h|h-1] + + // rotate left around b (left child) + // f (-2) [h+3] + // / \ + // [h+2] (-2) d g [h] + // / \ + // [h+1] b e [h|h-1] + // /\ + // [h] a c [h|h-1] + + // rotate right around f (root) + // d (0) [h+2] + // __________/\__________ + // / \ + // [h+1] (0|-1) b f (0|-1) [h+1] + // / \ /\ + // [h] a c [h|h-1] [h|h-1] e g [h] + + let node = node.UpdateLeftChild (Some (AVLTree.rotateLeft (node.LeftChild |> Option.get))) + AVLTree.rotateRight node + else + // Left child is balanced or left heavy + // rotate right around root + + // Illustration if left child is balanced + + // Initial state: + // d (-2) [h+3] + // / \ + // [h+2] (0) b e [h] + // / \ + // [h+1] a c [h+1] + + // rotate right around d (root) + // b (+1) [h+3] + // / \ + // [h+1] a d (-1) [h+2] + // / \ + // [h+1]c e [h] + + // Illustration if left child is left heavy + + // Initial state: + // d (-2) [h+3] + // / \ + // [h+2] (-1) b e [h] + // / \ + // [h+1] a c [h] + + // rotate right around d (root) + // b (0) [h+2] + // / \ + // [h+1] a d (0) [h+1] + // / \ + // [h] c e [h] + + AVLTree.rotateRight node + else + // Balance of root is within acceptable range + node + + let insert (value: int) (tree: AVLTree) : AVLTree = let rec insertImpl (maybeNode: Option) : AVLNode = match maybeNode with | None -> AVLNode.create value | Some node -> - let node = - if value < node.Value then - node.UpdateLeftChild (Some (insertImpl node.LeftChild)) - elif value = node.Value then - node - else - node.UpdateRightChild (Some (insertImpl node.RightChild)) - - if AVLNode.balanceFactor node > 1 then - // Root node is right heavy, current balance factor is 2 - // check the balance factor of the right child - if node.RightChild |> Option.get |> AVLNode.balanceFactor < 0 then - // Right child is left heavy - // rotate right around right child and rotate left around root - - // Illustration: possible heights are shown in brackets, - // and the balance factor of the node is shown in parentheses - - // Initial state: - // - // b (+2) [h+3] - // / \ - // [h] a f (-1) [h+2] - // /\ - // [h+1] (0|-1|+1) d g [h] - // /\ - // [h|h-1] c e [h|h-1] - - // rotate right around f (right child) - // - // b (+2) [h+3] - // / \ - // [h] a d (+1|+2) [h+2] - // /\ - // [h|h-1] c f (0|-1) [h+1] - // /\ - // [h|h-1] e g [h] - - // rotate left around b (root) - // d (0) [h+2] - // __________/\__________ - // / \ - // [h+1] (0|-1) b f (0|-1) [h+1] - // / \ /\ - // [h] a c [h|h-1] [h|h-1] e g [h] - - - let node =node.UpdateRightChild (Some (AVLTree.rotateRight (node.RightChild |> Option.get))) - AVLTree.rotateLeft node - else - // Right child is balanced or left heavy, - // rotate left around root - - // Illustration if right child is balanced - - // Initial state: - // b (+2) [h+3] - // / \ - // [h] a d (0) [h+2] - // /\ - // [h+1] c e [h+1] - - // rotate left around b (root) - // d (-1) [h+3] - // / \ - // [h+2] (+1) b e [h+1] - // / \ - // [h] a c [h+1] - - // Illustration if right child is right heavy - - // Initial state: - // b (+2) [h+3] - // / \ - // [h] a d (+1) [h+2] - // /\ - // [h] c e [h+1] - - // rotate left around b (root) - // d (0) [h+2] - // / \ - // [h+1] (0) b e [h+1] - // / \ - // [h] a c [h] - - AVLTree.rotateLeft node - elif AVLNode.balanceFactor node < -1 then - // Root node is left heavy, current balance factor is -2 - // check the balance factor of the left child - if node.LeftChild |> Option.get |> AVLNode.balanceFactor > 0 then - // Left child is right heavy - // rotate left around left child and rotate right around root - - // Initial state: - // f (-2) [h+3] - // / \ - // [h+2] (+1) b g [h] - // /\ - // [h] a d (0|-1|+1) [h+1] - // /\ - // [h|h-1] c e [h|h-1] - - // rotate left around b (left child) - // f (-2) [h+3] - // / \ - // [h+2] (-2) d g [h] - // / \ - // [h+1] b e [h|h-1] - // /\ - // [h] a c [h|h-1] - - // rotate right around f (root) - // d (0) [h+2] - // __________/\__________ - // / \ - // [h+1] (0|-1) b f (0|-1) [h+1] - // / \ /\ - // [h] a c [h|h-1] [h|h-1] e g [h] - - let node = node.UpdateLeftChild (Some (AVLTree.rotateLeft (node.LeftChild |> Option.get))) - AVLTree.rotateRight node - else - // Left child is balanced or left heavy - // rotate right around root - - // Illustration if left child is balanced - - // Initial state: - // d (-2) [h+3] - // / \ - // [h+2] (0) b e [h] - // / \ - // [h+1] a c [h+1] - - // rotate right around d (root) - // b (+1) [h+3] - // / \ - // [h+1] a d (-1) [h+2] - // / \ - // [h+1]c e [h] - - // Illustration if left child is left heavy - - // Initial state: - // d (-2) [h+3] - // / \ - // [h+2] (-1) b e [h] - // / \ - // [h+1] a c [h] - - // rotate right around d (root) - // b (0) [h+2] - // / \ - // [h+1] a d (0) [h+1] - // / \ - // [h] c e [h] - - AVLTree.rotateRight node - else - // Balance of root is within acceptable range + if value < node.Value then + node.UpdateLeftChild (Some (insertImpl node.LeftChild)) + |> rebalance + elif value = node.Value then node + else + node.UpdateRightChild (Some (insertImpl node.RightChild)) + |> rebalance insertImpl tree.Root |> fun root -> { Root = Some root } + let delete (value: int) (tree: AVLTree) : AVLTree = + let rec deleteMinValueNode (node: AVLNode) : Option * (* MinValue *) int = + match node.LeftChild with + | None -> + // delete current node and return the value that was replaced + node.RightChild, node.Value + | Some leftChild -> + let leftChild, minValue = deleteMinValueNode leftChild + let node = + node.UpdateLeftChild leftChild + |> rebalance + Some node, minValue + + let rec deleteImpl (maybeNode: Option) : Option = + match maybeNode with + | None -> None + | Some node -> + if value < node.Value then + node.UpdateLeftChild (deleteImpl node.LeftChild) + |> rebalance + |> Some + elif value = node.Value then + match node.LeftChild, node.RightChild with + | None, None -> None + | None, Some rightChild -> Some rightChild + | Some leftChild, None -> Some leftChild + | Some leftChild, Some rightChild -> + let rightNode, currentValue = deleteMinValueNode rightChild + let node = { node with Value = currentValue } + + node.UpdateRightChild rightNode + |> rebalance + |> Some + else + node.UpdateRightChild (deleteImpl node.RightChild) + |> rebalance + |> Some + + + deleteImpl tree.Root + |> fun root -> { Root = root } + + From 7044a83f1d4f65af7f8b1e246d67578963fa63ca Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Fri, 6 Dec 2024 22:49:20 +0600 Subject: [PATCH 5/7] add tests for delete --- Algorithms.Tests/DataStructures/AVLTree.fs | 148 ++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/Algorithms.Tests/DataStructures/AVLTree.fs b/Algorithms.Tests/DataStructures/AVLTree.fs index 7be2ba9..48826bc 100644 --- a/Algorithms.Tests/DataStructures/AVLTree.fs +++ b/Algorithms.Tests/DataStructures/AVLTree.fs @@ -170,4 +170,150 @@ type AVLTreeTests() = |> verifyProperties Assert.AreEqual(tree1.Root |> Option.map (fun n -> n.Value), - tree2.Root |> Option.map (fun n -> n.Value)) \ No newline at end of file + tree2.Root |> Option.map (fun n -> n.Value)) + [] + member _.``Delete maintains AVL properties``() = + let tree = + empty + |> insert 5 + |> verifyProperties + |> insert 3 + |> verifyProperties + |> insert 7 + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 9 + |> verifyProperties + |> insert 4 + |> verifyProperties + |> insert 6 + |> verifyProperties + |> insert 8 + |> verifyProperties + |> insert 2 + |> verifyProperties + |> delete 5 // Delete root + |> verifyProperties + |> delete 1 // Delete leaf + |> verifyProperties + |> delete 7 // Delete node with one child + |> verifyProperties + |> delete 3 // Delete node with two children + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + + [] + member _.``Delete from empty tree returns empty tree``() = + let tree = + empty + |> delete 1 + |> verifyProperties + + Assert.IsTrue(tree.Root.IsNone) + + [] + member _.``Delete non-existent value maintains tree structure``() = + let tree1 = + empty + |> insert 2 + |> verifyProperties + |> insert 1 + |> verifyProperties + |> insert 3 + |> verifyProperties + + let tree2 = + tree1 + |> delete 4 + |> verifyProperties + + Assert.AreEqual( + tree1.Root |> Option.map (fun n -> n.Value), + tree2.Root |> Option.map (fun n -> n.Value) + ) + + [] + member _.``Complex deletion cases maintain balance``() = + let tree = + empty + |> insert 50 // Create a more complex tree + |> verifyProperties + |> insert 25 + |> verifyProperties + |> insert 75 + |> verifyProperties + |> insert 10 + |> verifyProperties + |> insert 35 + |> verifyProperties + |> insert 60 + |> verifyProperties + |> insert 90 + |> verifyProperties + |> insert 5 + |> verifyProperties + |> insert 15 + |> verifyProperties + |> insert 30 + |> verifyProperties + |> insert 40 + |> verifyProperties + |> insert 55 + |> verifyProperties + |> insert 65 + |> verifyProperties + |> insert 80 + |> verifyProperties + |> insert 95 + |> verifyProperties + + // Test various deletion patterns + |> delete 50 // Delete root with two children + |> verifyProperties + |> delete 25 // Delete inner node with two children + |> verifyProperties + |> delete 5 // Delete leaf + |> verifyProperties + |> delete 95 // Delete leaf on opposite side + |> verifyProperties + |> delete 35 // Delete node with one child + |> verifyProperties + |> delete 75 // Delete node requiring rebalancing + |> verifyProperties + + Assert.IsTrue(tree.Root.IsSome) + + [] + member _.``Sequential deletion maintains balance``() = + let mutable tree = empty + + // Build tree with sequential inserts + for i in 1..10 do + tree <- insert i tree + tree <- verifyProperties tree + + // Delete in reverse order + for i in seq{10..(-1)..1} do + tree <- delete i tree + tree <- verifyProperties tree + + Assert.IsTrue(tree.Root.IsNone) + + [] + member _.``Random operations maintain AVL properties``() = + let rng = System.Random(42) + let mutable tree = empty + + // Random inserts + for _ in 1..20 do + let value = rng.Next(1, 100) + tree <- insert value tree + tree <- verifyProperties tree + + // Random deletes + for _ in 1..10 do + let value = rng.Next(1, 100) + tree <- delete value tree + tree <- verifyProperties tree \ No newline at end of file From 11792a06d762674296752e7bfaea78db9bd286b4 Mon Sep 17 00:00:00 2001 From: mahdihasnat Date: Fri, 6 Dec 2024 16:49:35 +0000 Subject: [PATCH 6/7] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index d333615..4c17128 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -2,6 +2,7 @@ ## Algorithms.Tests * Datastructures + * [Avltree](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/DataStructures/AVLTree.fs) * [Treap](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/DataStructures/Treap.fs) * [Trie](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/DataStructures/Trie.fs) * Math From 8c1e28bd0a345f82914ae3f4df4bda86e52317b4 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Fri, 6 Dec 2024 22:50:15 +0600 Subject: [PATCH 7/7] ignore unused variable --- Algorithms/DataStructures/AVLTree.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Algorithms/DataStructures/AVLTree.fs b/Algorithms/DataStructures/AVLTree.fs index 599db32..4043d54 100644 --- a/Algorithms/DataStructures/AVLTree.fs +++ b/Algorithms/DataStructures/AVLTree.fs @@ -262,7 +262,7 @@ module AVLTree = | None, None -> None | None, Some rightChild -> Some rightChild | Some leftChild, None -> Some leftChild - | Some leftChild, Some rightChild -> + | Some _leftChild, Some rightChild -> let rightNode, currentValue = deleteMinValueNode rightChild let node = { node with Value = currentValue }