Skip to content

Add utility functions to traverse trees to find minimum, maximum node and the keys adjacent to a node #13

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 7 commits into from
Feb 26, 2025
57 changes: 57 additions & 0 deletions src/binarytree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,60 @@ function _printkeyvalue(io::IO, node::BinaryNode)
show(ioctx, val)
end
end

# -----------
# UTILITIES
# -----------

minnode(tree::BinaryTree) = minnode(root(tree))

function minnode(node::BinaryNode)
leftnode = left(node)
isnothing(leftnode) ? node : minnode(leftnode)
end

minnode(node::Nothing) = nothing

maxnode(tree::BinaryTree) = maxnode(root(tree))

function maxnode(node::BinaryNode)
rightnode = right(node)
isnothing(rightnode) ? node : maxnode(rightnode)
end

maxnode(node::Nothing) = nothing

function prevnext(tree::BinaryTree, k)
prev, next = nothing, nothing
current = root(tree)
# traverse from the root to the target node, updating candidates
while !isnothing(current) && key(current) != k
if k < key(current)
# current is a potential next (successor)
next = current
current = left(current)
else # k > key(current)
# current is a potential previous (predecessor)
prev = current
current = right(current)
end
end

# if the node wasn't found, return the best candidate values
if isnothing(current)
return (prev, next)
end

# if there is a left subtree, the true previous (predecessor) is the maximum in that subtree
if !isnothing(left(current))
prev = maxnode(left(current))
end
# similarly, if there is a right subtree, the true next (successor) is the minimum in that subtree
if !isnothing(right(current))
next = minnode(right(current))
end

(prev, next)
end

prevnext(tree::BinaryTree, k::Nothing) = (nothing, nothing)
43 changes: 42 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const BT = BinaryTrees
BT.insert!(tree, 2, 20)
BT.insert!(tree, 1, 10)
BT.insert!(tree, 3, 30)
# deleting a key that does not exist
# deleting a key that does not exist
# does not change the tree
BT.delete!(tree, 4)
@test tree === tree
Expand Down Expand Up @@ -219,11 +219,34 @@ const BT = BinaryTrees
@test BT.value(BT.search(tree, (0, 0, 1))) == 1
@test BT.value(BT.search(tree, (1, 0, 0))) == 3

# traversal algorithms
tree = AVLTree{Int,Float64}()
BT.insert!(tree, 0, 5)
BT.insert!(tree, 1, 6)
BT.insert!(tree, 2, 8)
BT.insert!(tree, 3, 10)
BT.insert!(tree, 4, 20)
BT.insert!(tree, 5, 30)
BT.insert!(tree, 6, 40)
@test BT.key(BT.minnode(tree)) == 0
@test BT.key(BT.maxnode(tree)) == 6
@test BT.prevnext(tree, 0)[1] == nothing
@test BT.key.(BT.prevnext(tree, 2)) == (1, 3)
@test BT.key.(BT.prevnext(tree, 5)) == (4, 6)
@test BT.prevnext(tree, nothing) == (nothing, nothing)

# type stability
tree = AVLTree{Int,Int}()
@inferred BT.insert!(tree, 2, 20)
@inferred BT.insert!(tree, 1, 10)
@inferred BT.insert!(tree, 3, 30)
@inferred Nothing BT.minnode(tree)
@inferred Nothing BT.maxnode(tree)
@inferred Tuple{Union{BT.AVLNode,Nothing},Union{BT.AVLNode,Nothing}} BT.prevnext(
tree,
2
)
@inferred BT.prevnext(tree, nothing)
@inferred Nothing BT.search(tree, 2)
@inferred Nothing BT.search(tree, 1)
@inferred Nothing BT.search(tree, 3)
Expand All @@ -234,6 +257,12 @@ const BT = BinaryTrees
@inferred BT.insert!(tree, 2)
@inferred BT.insert!(tree, 1)
@inferred BT.insert!(tree, 3)
@inferred Nothing BT.minnode(tree)
@inferred Nothing BT.maxnode(tree)
@inferred Tuple{Union{BT.AVLNode,Nothing},Union{BT.AVLNode,Nothing}} BT.prevnext(
tree,
2
)
@inferred Nothing BT.search(tree, 2)
@inferred Nothing BT.search(tree, 1)
@inferred Nothing BT.search(tree, 3)
Expand All @@ -244,6 +273,12 @@ const BT = BinaryTrees
@inferred BT.insert!(tree, "key2", 2)
@inferred BT.insert!(tree, "key1", 1)
@inferred BT.insert!(tree, "key3", 3)
@inferred Nothing BT.minnode(tree)
@inferred Nothing BT.maxnode(tree)
@inferred Tuple{Union{BT.AVLNode,Nothing},Union{BT.AVLNode,Nothing}} BT.prevnext(
tree,
"key2"
)
@inferred Nothing BT.search(tree, "key2")
@inferred Nothing BT.search(tree, "key1")
@inferred Nothing BT.search(tree, "key3")
Expand All @@ -254,6 +289,12 @@ const BT = BinaryTrees
@inferred BT.insert!(tree, (0, 1, 0), 2)
@inferred BT.insert!(tree, (0, 0, 1), 1)
@inferred BT.insert!(tree, (1, 0, 0), 3)
@inferred Nothing BT.minnode(tree)
@inferred Nothing BT.maxnode(tree)
@inferred Tuple{Union{BT.AVLNode,Nothing},Union{BT.AVLNode,Nothing}} BT.prevnext(
tree,
(0, 1, 0)
)
@inferred Nothing BT.search(tree, (0, 1, 0))
@inferred Nothing BT.search(tree, (0, 0, 1))
@inferred Nothing BT.search(tree, (1, 0, 0))
Expand Down