Skip to content

Add treap data structure and related tests #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions Algorithms.Tests/DataStructures/Treap.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
namespace Algorithms.Tests.DataStructures

open Microsoft.VisualStudio.TestTools.UnitTesting
open Algorithms.DataStructures.Treap

[<TestClass>]
type TreapTests () =

[<TestMethod>]
member this.``Test basic operations``() =
let treap =
empty
|> insert 5
|> insert 3
|> insert 7

// 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)

[<TestMethod>]
member this.``Test empty treap``() =
let treap = empty
Assert.AreEqual(None, getKthElement treap 0u)
Assert.AreEqual(None, getIndex treap 5)

[<TestMethod>]
member this.``Test deletion``() =
let mutable treap =
empty
|> insert 5
|> insert 3
|> insert 7

// 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)

[<TestMethod>]
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)


[<TestMethod>]
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)
124 changes: 124 additions & 0 deletions Algorithms/DataStructures/Treap.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
namespace Algorithms.DataStructures

module Treap =
type TreapNode = {
Value: int
Priority: int64
Size: int
LeftChild: Option<TreapNode>
RightChild: Option<TreapNode>
}

module TreapNode =
let create (value: int) : TreapNode =
{
Value = value
Priority = System.Random().NextInt64()
Size = 1
LeftChild = None
RightChild = None
}

let getSize (maybeNode: Option<TreapNode>) : int =
maybeNode
|> Option.map (fun node -> node.Size)
|> Option.defaultValue 0

type TreapNode
with
member this.UpdateLeftChild (leftChild: Option<TreapNode>) : TreapNode =
{
this with
LeftChild = leftChild
Size = 1 + TreapNode.getSize leftChild + TreapNode.getSize this.RightChild
}
member this.UpdateRightChild (rightChild: Option<TreapNode>) : TreapNode =
{
this with
RightChild = rightChild
Size = 1 + TreapNode.getSize this.LeftChild + TreapNode.getSize rightChild
}

[<RequireQualifiedAccess>]
type Treap = {
Root: Option<TreapNode>
}

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<TreapNode>) (value: int) : Option<TreapNode> * Option<TreapNode> =
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<TreapNode>) (right: Option<TreapNode>) : Option<TreapNode> =
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

// 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 }

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<int> =
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<int> =
let left, right = split treap.Root value
let node, _right = split right (value + 1)
node
|> Option.map (fun _ -> TreapNode.getSize left)
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading