Skip to content

Commit 06f8663

Browse files
committed
Add treap data structure and related tests
1 parent b478a6a commit 06f8663

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 mutable treap = empty
12+
13+
// Test insertion
14+
treap <- insert 5 treap
15+
treap <- insert 3 treap
16+
treap <- insert 7 treap
17+
18+
// Test getKthElement (0-based indexing)
19+
Assert.AreEqual(Some 3, getKthElement treap 0u)
20+
Assert.AreEqual(Some 5, getKthElement treap 1u)
21+
Assert.AreEqual(Some 7, getKthElement treap 2u)
22+
Assert.AreEqual(None, getKthElement treap 3u)
23+
24+
// Test getIndex
25+
Assert.AreEqual(Some 0, getIndex treap 3)
26+
Assert.AreEqual(Some 1, getIndex treap 5)
27+
Assert.AreEqual(Some 2, getIndex treap 7)
28+
Assert.AreEqual(None, getIndex treap 4)
29+
30+
[<TestMethod>]
31+
member this.``Test empty treap``() =
32+
let treap = empty
33+
Assert.AreEqual(None, getKthElement treap 0u)
34+
Assert.AreEqual(None, getIndex treap 5)
35+
36+
[<TestMethod>]
37+
member this.``Test deletion``() =
38+
let mutable treap = empty
39+
treap <- insert 5 treap
40+
treap <- insert 3 treap
41+
treap <- insert 7 treap
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 order preservation``() =
56+
let mutable treap = empty
57+
58+
// Insert in non-sorted order
59+
treap <- insert 8 treap
60+
treap <- insert 3 treap
61+
treap <- insert 10 treap
62+
treap <- insert 1 treap
63+
treap <- insert 6 treap
64+
treap <- insert 4 treap
65+
treap <- insert 7 treap
66+
treap <- insert 14 treap
67+
treap <- insert 13 treap
68+
69+
// Verify elements are retrievable in sorted order
70+
for i in 0u..8u do
71+
let expected =
72+
match i with
73+
| 0u -> 1 | 1u -> 3 | 2u -> 4 | 3u -> 6 | 4u -> 7
74+
| 5u -> 8 | 6u -> 10 | 7u -> 13 | 8u -> 14
75+
| _ -> failwith "Unexpected index"
76+
Assert.AreEqual(Some expected, getKthElement treap i)

Algorithms/DataStructures/Treap.fs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
let insert (value: int) (treap: Treap) : Treap =
86+
let node = TreapNode.create value
87+
let left, right = split treap.Root value
88+
merge (merge left (Some node)) right
89+
|> fun root -> { Root = root }
90+
91+
let erase (value: int) (treap: Treap) : Treap =
92+
let left, right = split treap.Root value
93+
let _, right = split right (value + 1)
94+
merge left right
95+
|> fun root -> { Root = root }
96+
97+
/// Gets the kth smallest element in the treap (0-indexed)
98+
/// Returns None if k is out of bounds
99+
let getKthElement (treap: Treap) (k: uint) : Option<int> =
100+
if TreapNode.getSize treap.Root |> uint <= k then
101+
None
102+
else
103+
let rec getKthElementImpl (root: TreapNode) (k: int) : int =
104+
assert (k < root.Size)
105+
if root.Size = 1 then
106+
root.Value
107+
else
108+
if k < TreapNode.getSize root.LeftChild then
109+
getKthElementImpl (root.LeftChild |> Option.get) k
110+
elif k = TreapNode.getSize root.LeftChild then
111+
root.Value
112+
else
113+
getKthElementImpl (root.RightChild |> Option.get) (k - TreapNode.getSize root.LeftChild - 1)
114+
getKthElementImpl (treap.Root |> Option.get) (int k) |> Some
115+
116+
/// Gets the index of a value in the treap (0 indexed position in sorted order)
117+
/// Returns None if value is not found
118+
let getIndex (treap: Treap) (value: int) : Option<int> =
119+
let left, right = split treap.Root value
120+
let node, _right = split right (value + 1)
121+
node
122+
|> Option.map (fun _ -> TreapNode.getSize left)

0 commit comments

Comments
 (0)