From 06f8663b42efc49bf1eec50518b7299b5fe30563 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Tue, 26 Nov 2024 01:18:49 +0600 Subject: [PATCH 1/3] Add treap data structure and related tests --- Algorithms.Tests/DataStructures/Treap.fs | 76 ++++++++++++++ Algorithms/DataStructures/Treap.fs | 122 +++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 Algorithms.Tests/DataStructures/Treap.fs create mode 100644 Algorithms/DataStructures/Treap.fs diff --git a/Algorithms.Tests/DataStructures/Treap.fs b/Algorithms.Tests/DataStructures/Treap.fs new file mode 100644 index 0000000..8d1db2c --- /dev/null +++ b/Algorithms.Tests/DataStructures/Treap.fs @@ -0,0 +1,76 @@ +namespace Algorithms.Tests.DataStructures + +open Microsoft.VisualStudio.TestTools.UnitTesting +open Algorithms.DataStructures.Treap + +[] +type TreapTests () = + + [] + member this.``Test basic operations``() = + let mutable treap = empty + + // Test insertion + treap <- insert 5 treap + treap <- insert 3 treap + treap <- insert 7 treap + + // Test getKthElement (0-based indexing) + Assert.AreEqual(Some 3, getKthElement treap 0u) + Assert.AreEqual(Some 5, getKthElement treap 1u) + Assert.AreEqual(Some 7, getKthElement treap 2u) + Assert.AreEqual(None, getKthElement treap 3u) + + // Test getIndex + Assert.AreEqual(Some 0, getIndex treap 3) + Assert.AreEqual(Some 1, getIndex treap 5) + Assert.AreEqual(Some 2, getIndex treap 7) + Assert.AreEqual(None, getIndex treap 4) + + [] + member this.``Test empty treap``() = + let treap = empty + Assert.AreEqual(None, getKthElement treap 0u) + Assert.AreEqual(None, getIndex treap 5) + + [] + member this.``Test deletion``() = + let mutable treap = empty + treap <- insert 5 treap + treap <- insert 3 treap + treap <- insert 7 treap + + // Delete middle element + treap <- erase 5 treap + Assert.AreEqual(Some 3, getKthElement treap 0u) + Assert.AreEqual(Some 7, getKthElement treap 1u) + Assert.AreEqual(None, getKthElement treap 2u) + + // Delete non-existent element + treap <- erase 5 treap + Assert.AreEqual(Some 3, getKthElement treap 0u) + Assert.AreEqual(Some 7, getKthElement treap 1u) + + [] + member this.``Test order preservation``() = + let mutable treap = empty + + // Insert in non-sorted order + treap <- insert 8 treap + treap <- insert 3 treap + treap <- insert 10 treap + treap <- insert 1 treap + treap <- insert 6 treap + treap <- insert 4 treap + treap <- insert 7 treap + treap <- insert 14 treap + treap <- insert 13 treap + + // Verify elements are retrievable in sorted order + for i in 0u..8u do + let expected = + match i with + | 0u -> 1 | 1u -> 3 | 2u -> 4 | 3u -> 6 | 4u -> 7 + | 5u -> 8 | 6u -> 10 | 7u -> 13 | 8u -> 14 + | _ -> failwith "Unexpected index" + Assert.AreEqual(Some expected, getKthElement treap i) diff --git a/Algorithms/DataStructures/Treap.fs b/Algorithms/DataStructures/Treap.fs new file mode 100644 index 0000000..5b45b0a --- /dev/null +++ b/Algorithms/DataStructures/Treap.fs @@ -0,0 +1,122 @@ +namespace Algorithms.DataStructures + +module Treap = + type TreapNode = { + Value: int + Priority: int64 + Size: int + LeftChild: Option + RightChild: Option + } + + module TreapNode = + let create (value: int) : TreapNode = + { + Value = value + Priority = System.Random().NextInt64() + Size = 1 + LeftChild = None + RightChild = None + } + + let getSize (maybeNode: Option) : int = + maybeNode + |> Option.map (fun node -> node.Size) + |> Option.defaultValue 0 + + type TreapNode + with + member this.UpdateLeftChild (leftChild: Option) : TreapNode = + { + this with + LeftChild = leftChild + Size = 1 + TreapNode.getSize leftChild + TreapNode.getSize this.RightChild + } + member this.UpdateRightChild (rightChild: Option) : TreapNode = + { + this with + RightChild = rightChild + Size = 1 + TreapNode.getSize this.LeftChild + TreapNode.getSize rightChild + } + + [] + type Treap = { + Root: Option + } + + let empty : Treap = { Root = None } + + /// Splits treap into two parts based on value + /// Returns (left, right) tuple where: + /// - left contains all nodes with values < split value + /// - right contains all nodes with values >= split value + let rec split (root: Option) (value: int) : Option * Option = + match root with + | None -> + None, None + | Some root -> + if root.Value < value then + // root node belongs to the left + // split the right child of root and update the right child of root + let updatedRightOfRoot, right = split root.RightChild value + root.UpdateRightChild updatedRightOfRoot |> Some, right + else + // root node belongs to the right + // split the left child of root and update the left child of root + let left, updatedLeftOfRoot = split root.LeftChild value + left, root.UpdateLeftChild updatedLeftOfRoot |> Some + + /// Merges two treaps maintaining BST and heap properties + /// Assumes all values in left treap are less than all values in right treap + let rec merge (left: Option) (right: Option) : Option = + match left, right with + | None, right -> right + | left, None -> left + | Some left, Some right -> + if left.Priority < right.Priority then + // left node is the root of the merged treap, merge its right child with right treap + let updatedLeftsRightChild = merge left.RightChild (Some right) + left.UpdateRightChild updatedLeftsRightChild |> Some + else + // right node is the root of the merged treap, merge its left child with left treap + let updatedRightsLeftChild = merge (Some left) right.LeftChild + right.UpdateLeftChild updatedRightsLeftChild |> Some + + let insert (value: int) (treap: Treap) : Treap = + let node = TreapNode.create value + let left, right = split treap.Root value + merge (merge left (Some node)) right + |> fun root -> { Root = root } + + let erase (value: int) (treap: Treap) : Treap = + let left, right = split treap.Root value + let _, right = split right (value + 1) + merge left right + |> fun root -> { Root = root } + + /// Gets the kth smallest element in the treap (0-indexed) + /// Returns None if k is out of bounds + let getKthElement (treap: Treap) (k: uint) : Option = + if TreapNode.getSize treap.Root |> uint <= k then + None + else + let rec getKthElementImpl (root: TreapNode) (k: int) : int = + assert (k < root.Size) + if root.Size = 1 then + root.Value + else + if k < TreapNode.getSize root.LeftChild then + getKthElementImpl (root.LeftChild |> Option.get) k + elif k = TreapNode.getSize root.LeftChild then + root.Value + else + getKthElementImpl (root.RightChild |> Option.get) (k - TreapNode.getSize root.LeftChild - 1) + getKthElementImpl (treap.Root |> Option.get) (int k) |> Some + + /// Gets the index of a value in the treap (0 indexed position in sorted order) + /// Returns None if value is not found + let getIndex (treap: Treap) (value: int) : Option = + let left, right = split treap.Root value + let node, _right = split right (value + 1) + node + |> Option.map (fun _ -> TreapNode.getSize left) \ No newline at end of file From 4dff03735e54d1a63fbf1f86f9a3b4709840ba38 Mon Sep 17 00:00:00 2001 From: mahdihasnat Date: Mon, 25 Nov 2024 19:19:07 +0000 Subject: [PATCH 2/3] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index cd14151..e354294 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -2,6 +2,7 @@ ## Algorithms.Tests * Datastructures + * [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 * [Absmaxtests](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/Math/AbsMaxTests.fs) @@ -40,6 +41,7 @@ ## Algorithms * Datastructures + * [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 * [Abs](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/Math/Abs.fs) From 93679e44a0e00fb7803a9f910340c29e022f1fb5 Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam Date: Tue, 26 Nov 2024 06:05:14 +0600 Subject: [PATCH 3/3] Fix duplicate insertion in treap --- Algorithms.Tests/DataStructures/Treap.fs | 39 ++++++++++++++++++------ Algorithms/DataStructures/Treap.fs | 2 ++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Algorithms.Tests/DataStructures/Treap.fs b/Algorithms.Tests/DataStructures/Treap.fs index 8d1db2c..50b252f 100644 --- a/Algorithms.Tests/DataStructures/Treap.fs +++ b/Algorithms.Tests/DataStructures/Treap.fs @@ -8,12 +8,11 @@ type TreapTests () = [] member this.``Test basic operations``() = - let mutable treap = empty - - // Test insertion - treap <- insert 5 treap - treap <- insert 3 treap - treap <- insert 7 treap + let treap = + empty + |> insert 5 + |> insert 3 + |> insert 7 // Test getKthElement (0-based indexing) Assert.AreEqual(Some 3, getKthElement treap 0u) @@ -35,10 +34,11 @@ type TreapTests () = [] member this.``Test deletion``() = - let mutable treap = empty - treap <- insert 5 treap - treap <- insert 3 treap - treap <- insert 7 treap + let mutable treap = + empty + |> insert 5 + |> insert 3 + |> insert 7 // Delete middle element treap <- erase 5 treap @@ -51,6 +51,25 @@ type TreapTests () = Assert.AreEqual(Some 3, getKthElement treap 0u) Assert.AreEqual(Some 7, getKthElement treap 1u) + [] + member this.``Test duplicate insertion``() = + let mutable treap = empty + treap <- insert 3 treap + treap <- insert 5 treap + treap <- insert 1 treap + treap <- insert 3 treap + + Assert.AreEqual(Some 1, getKthElement treap 0u) + Assert.AreEqual(Some 3, getKthElement treap 1u) + Assert.AreEqual(Some 5, getKthElement treap 2u) + Assert.AreEqual(None, getKthElement treap 3u) + + treap <- erase 3 treap + Assert.AreEqual(Some 1, getKthElement treap 0u) + Assert.AreEqual(Some 5, getKthElement treap 1u) + Assert.AreEqual(None, getKthElement treap 2u) + + [] member this.``Test order preservation``() = let mutable treap = empty diff --git a/Algorithms/DataStructures/Treap.fs b/Algorithms/DataStructures/Treap.fs index 5b45b0a..3d9f66c 100644 --- a/Algorithms/DataStructures/Treap.fs +++ b/Algorithms/DataStructures/Treap.fs @@ -82,9 +82,11 @@ module Treap = let updatedRightsLeftChild = merge (Some left) right.LeftChild right.UpdateLeftChild updatedRightsLeftChild |> Some + // Inserts a new value into the treap, noop if value already exists let insert (value: int) (treap: Treap) : Treap = let node = TreapNode.create value let left, right = split treap.Root value + let _, right = split right (value + 1) merge (merge left (Some node)) right |> fun root -> { Root = root }