Skip to content
86 changes: 86 additions & 0 deletions src/binarytree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,89 @@ function _printkeyvalue(io::IO, node::BinaryNode)
show(ioctx, val)
end
end

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

"""
BinaryTrees.minnode(tree)

Find the `node` with the smallest `key` in the `tree`.

BinaryTrees.minnode(node)

Find the `node` with the smallest `key` in the subtree rooted at `node`.
If `nothing` is provided, `nothing` is returned.
"""
minnode(tree::BinaryTree) = minnode(root(tree))

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

minnode(node::Nothing) = nothing

"""
BinaryTrees.maxnode(tree)

Find the `node` with the maximum `key` in the `tree`.

BinaryTrees.maxnode(node)

Find the `node` with the maximum `key` in the subtree rooted at `node`.
If `nothing` is provided, `nothing` is returned.
"""
maxnode(tree::BinaryTree) = maxnode(root(tree))

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

maxnode(node::Nothing) = nothing

"""
BinaryTrees.prevnext(tree, k)

Returns a `tuple` of each `node` immediately before
and after the `node` with `key`, `k` within `tree`.

If an adjacent `node` does not exist, `nothing` is returned in its place.
If `k` is `nothing`, returns `(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)
38 changes: 37 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.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 Nothing BT.prevnext(tree, 2)[1]
@inferred Nothing BT.prevnext(tree, 2)[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 +255,11 @@ 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 Nothing BT.prevnext(tree, 2)[1]
@inferred Nothing BT.prevnext(tree, 2)[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 @@ -244,6 +270,11 @@ 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 Nothing BT.prevnext(tree, "key2")[1]
@inferred Nothing BT.prevnext(tree, "key2")[2]
@inferred BT.prevnext(tree, nothing)
@inferred Nothing BT.search(tree, "key2")
@inferred Nothing BT.search(tree, "key1")
@inferred Nothing BT.search(tree, "key3")
Expand All @@ -254,6 +285,11 @@ 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 Nothing BT.prevnext(tree, (0, 1, 0))[1]
@inferred Nothing BT.prevnext(tree, (0, 1, 0))[2]
@inferred BT.prevnext(tree, nothing)
@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