Skip to content

Commit 824fa27

Browse files
authored
Add treap data structure and related tests (#31)
* Add treap data structure and related tests * updating DIRECTORY.md * Fix duplicate insertion in treap --------- Co-authored-by: mahdihasnat <[email protected]>
1 parent b478a6a commit 824fa27

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
namespace Algorithms.Tests.DataStructures
2+
3+
open Microsoft.VisualStudio.TestTools.UnitTesting
4+
open Algorithms.DataStructures.Treap
5+
6+
[<TestClass>]
7+
type TreapTests () =
8+
9+
[<TestMethod>]
10+
member this.``Test basic operations``() =
11+
let treap =
12+
empty
13+
|> insert 5
14+
|> insert 3
15+
|> insert 7
16+
17+
// Test getKthElement (0-based indexing)
18+
Assert.AreEqual(Some 3, getKthElement treap 0u)
19+
Assert.AreEqual(Some 5, getKthElement treap 1u)
20+
Assert.AreEqual(Some 7, getKthElement treap 2u)
21+
Assert.AreEqual(None, getKthElement treap 3u)
22+
23+
// Test getIndex
24+
Assert.AreEqual(Some 0, getIndex treap 3)
25+
Assert.AreEqual(Some 1, getIndex treap 5)
26+
Assert.AreEqual(Some 2, getIndex treap 7)
27+
Assert.AreEqual(None, getIndex treap 4)
28+
29+
[<TestMethod>]
30+
member this.``Test empty treap``() =
31+
let treap = empty
32+
Assert.AreEqual(None, getKthElement treap 0u)
33+
Assert.AreEqual(None, getIndex treap 5)
34+
35+
[<TestMethod>]
36+
member this.``Test deletion``() =
37+
let mutable treap =
38+
empty
39+
|> insert 5
40+
|> insert 3
41+
|> insert 7
42+
43+
// Delete middle element
44+
treap <- erase 5 treap
45+
Assert.AreEqual(Some 3, getKthElement treap 0u)
46+
Assert.AreEqual(Some 7, getKthElement treap 1u)
47+
Assert.AreEqual(None, getKthElement treap 2u)
48+
49+
// Delete non-existent element
50+
treap <- erase 5 treap
51+
Assert.AreEqual(Some 3, getKthElement treap 0u)
52+
Assert.AreEqual(Some 7, getKthElement treap 1u)
53+
54+
[<TestMethod>]
55+
member this.``Test duplicate insertion``() =
56+
let mutable treap = empty
57+
treap <- insert 3 treap
58+
treap <- insert 5 treap
59+
treap <- insert 1 treap
60+
treap <- insert 3 treap
61+
62+
Assert.AreEqual(Some 1, getKthElement treap 0u)
63+
Assert.AreEqual(Some 3, getKthElement treap 1u)
64+
Assert.AreEqual(Some 5, getKthElement treap 2u)
65+
Assert.AreEqual(None, getKthElement treap 3u)
66+
67+
treap <- erase 3 treap
68+
Assert.AreEqual(Some 1, getKthElement treap 0u)
69+
Assert.AreEqual(Some 5, getKthElement treap 1u)
70+
Assert.AreEqual(None, getKthElement treap 2u)
71+
72+
73+
[<TestMethod>]
74+
member this.``Test order preservation``() =
75+
let mutable treap = empty
76+
77+
// Insert in non-sorted order
78+
treap <- insert 8 treap
79+
treap <- insert 3 treap
80+
treap <- insert 10 treap
81+
treap <- insert 1 treap
82+
treap <- insert 6 treap
83+
treap <- insert 4 treap
84+
treap <- insert 7 treap
85+
treap <- insert 14 treap
86+
treap <- insert 13 treap
87+
88+
// Verify elements are retrievable in sorted order
89+
for i in 0u..8u do
90+
let expected =
91+
match i with
92+
| 0u -> 1 | 1u -> 3 | 2u -> 4 | 3u -> 6 | 4u -> 7
93+
| 5u -> 8 | 6u -> 10 | 7u -> 13 | 8u -> 14
94+
| _ -> failwith "Unexpected index"
95+
Assert.AreEqual(Some expected, getKthElement treap i)

Algorithms/DataStructures/Treap.fs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
namespace Algorithms.DataStructures
2+
3+
module Treap =
4+
type TreapNode = {
5+
Value: int
6+
Priority: int64
7+
Size: int
8+
LeftChild: Option<TreapNode>
9+
RightChild: Option<TreapNode>
10+
}
11+
12+
module TreapNode =
13+
let create (value: int) : TreapNode =
14+
{
15+
Value = value
16+
Priority = System.Random().NextInt64()
17+
Size = 1
18+
LeftChild = None
19+
RightChild = None
20+
}
21+
22+
let getSize (maybeNode: Option<TreapNode>) : int =
23+
maybeNode
24+
|> Option.map (fun node -> node.Size)
25+
|> Option.defaultValue 0
26+
27+
type TreapNode
28+
with
29+
member this.UpdateLeftChild (leftChild: Option<TreapNode>) : TreapNode =
30+
{
31+
this with
32+
LeftChild = leftChild
33+
Size = 1 + TreapNode.getSize leftChild + TreapNode.getSize this.RightChild
34+
}
35+
member this.UpdateRightChild (rightChild: Option<TreapNode>) : TreapNode =
36+
{
37+
this with
38+
RightChild = rightChild
39+
Size = 1 + TreapNode.getSize this.LeftChild + TreapNode.getSize rightChild
40+
}
41+
42+
[<RequireQualifiedAccess>]
43+
type Treap = {
44+
Root: Option<TreapNode>
45+
}
46+
47+
let empty : Treap = { Root = None }
48+
49+
/// Splits treap into two parts based on value
50+
/// Returns (left, right) tuple where:
51+
/// - left contains all nodes with values < split value
52+
/// - right contains all nodes with values >= split value
53+
let rec split (root: Option<TreapNode>) (value: int) : Option<TreapNode> * Option<TreapNode> =
54+
match root with
55+
| None ->
56+
None, None
57+
| Some root ->
58+
if root.Value < value then
59+
// root node belongs to the left
60+
// split the right child of root and update the right child of root
61+
let updatedRightOfRoot, right = split root.RightChild value
62+
root.UpdateRightChild updatedRightOfRoot |> Some, right
63+
else
64+
// root node belongs to the right
65+
// split the left child of root and update the left child of root
66+
let left, updatedLeftOfRoot = split root.LeftChild value
67+
left, root.UpdateLeftChild updatedLeftOfRoot |> Some
68+
69+
/// Merges two treaps maintaining BST and heap properties
70+
/// Assumes all values in left treap are less than all values in right treap
71+
let rec merge (left: Option<TreapNode>) (right: Option<TreapNode>) : Option<TreapNode> =
72+
match left, right with
73+
| None, right -> right
74+
| left, None -> left
75+
| Some left, Some right ->
76+
if left.Priority < right.Priority then
77+
// left node is the root of the merged treap, merge its right child with right treap
78+
let updatedLeftsRightChild = merge left.RightChild (Some right)
79+
left.UpdateRightChild updatedLeftsRightChild |> Some
80+
else
81+
// right node is the root of the merged treap, merge its left child with left treap
82+
let updatedRightsLeftChild = merge (Some left) right.LeftChild
83+
right.UpdateLeftChild updatedRightsLeftChild |> Some
84+
85+
// Inserts a new value into the treap, noop if value already exists
86+
let insert (value: int) (treap: Treap) : Treap =
87+
let node = TreapNode.create value
88+
let left, right = split treap.Root value
89+
let _, right = split right (value + 1)
90+
merge (merge left (Some node)) right
91+
|> fun root -> { Root = root }
92+
93+
let erase (value: int) (treap: Treap) : Treap =
94+
let left, right = split treap.Root value
95+
let _, right = split right (value + 1)
96+
merge left right
97+
|> fun root -> { Root = root }
98+
99+
/// Gets the kth smallest element in the treap (0-indexed)
100+
/// Returns None if k is out of bounds
101+
let getKthElement (treap: Treap) (k: uint) : Option<int> =
102+
if TreapNode.getSize treap.Root |> uint <= k then
103+
None
104+
else
105+
let rec getKthElementImpl (root: TreapNode) (k: int) : int =
106+
assert (k < root.Size)
107+
if root.Size = 1 then
108+
root.Value
109+
else
110+
if k < TreapNode.getSize root.LeftChild then
111+
getKthElementImpl (root.LeftChild |> Option.get) k
112+
elif k = TreapNode.getSize root.LeftChild then
113+
root.Value
114+
else
115+
getKthElementImpl (root.RightChild |> Option.get) (k - TreapNode.getSize root.LeftChild - 1)
116+
getKthElementImpl (treap.Root |> Option.get) (int k) |> Some
117+
118+
/// Gets the index of a value in the treap (0 indexed position in sorted order)
119+
/// Returns None if value is not found
120+
let getIndex (treap: Treap) (value: int) : Option<int> =
121+
let left, right = split treap.Root value
122+
let node, _right = split right (value + 1)
123+
node
124+
|> Option.map (fun _ -> TreapNode.getSize left)

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Algorithms.Tests
44
* Datastructures
5+
* [Treap](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/DataStructures/Treap.fs)
56
* [Trie](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/DataStructures/Trie.fs)
67
* Math
78
* [Absmaxtests](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms.Tests/Math/AbsMaxTests.fs)
@@ -40,6 +41,7 @@
4041

4142
## Algorithms
4243
* Datastructures
44+
* [Treap](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/DataStructures/Treap.fs)
4345
* [Trie](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/DataStructures/Trie.fs)
4446
* Math
4547
* [Abs](https://github.com/TheAlgorithms/F-Sharp/blob/main/Algorithms/Math/Abs.fs)

0 commit comments

Comments
 (0)