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
64 changes: 64 additions & 0 deletions src/binarytree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,67 @@ 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 abovebelow(tree::BinaryNode, x::BinaryNode)
above, below = nothing, nothing
current = tree
# Traverse from the root to the target node, updating candidates.
while !isnothing(current) && key(current) != key(x)
if key(x) < key(current)
# current is a potential above (successor)
above = current
current = left(current)
else # x.key > current.key
# current is a potential below (predecessor)
below = current
current = right(current)
end
end

# If the node wasn't found, return the best candidate values
if isnothing(current)
return (above, below)
end

# Found the node with key equal to x.key.
# Now, if there is a left subtree, the true below (predecessor) is the maximum in that subtree.
if !isnothing(left(current))
below = maxnode(left(current))
end
# Similarly, if there is a right subtree, the true above (successor) is the minimum in that subtree.
if !isnothing(right(current))
above = minnode(right(current))
end

(above, below)
end

function abovebelow(tree::BinaryTree, x::BinaryNode)
abovebelow(root(tree), x)
end

function abovebelow(tree, x::Nothing)
(nothing, nothing)
end
23 changes: 22 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,32 @@ 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.abovebelow(tree, BT.AVLNode(0, 5))[2] == nothing
@test BT.key.(BT.abovebelow(tree, BT.AVLNode(2, 10))) == (3, 1)
@test BT.key.(BT.abovebelow(BT.root(tree), BT.AVLNode(2, 10))) == (3, 1)
@test BT.key.(BT.abovebelow(tree, BT.AVLNode(5, 30))) == (6, 4)
@test BT.abovebelow(tree, nothing) == (nothing, nothing)
@test BT.abovebelow(BT.root(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 BT.minnode(tree)
@inferred BT.maxnode(tree)
@inferred BT.abovebelow(tree, BT.AVLNode(2, 20))
@inferred Nothing BT.search(tree, 2)
@inferred Nothing BT.search(tree, 1)
@inferred Nothing BT.search(tree, 3)
Expand Down
Loading