diff --git a/src/binarytree.jl b/src/binarytree.jl index d5b40da..6e0c240 100644 --- a/src/binarytree.jl +++ b/src/binarytree.jl @@ -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 `tree` node with key `k`. + +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) diff --git a/test/runtests.jl b/test/runtests.jl index fc18830..4732f6b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -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) @@ -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) @@ -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") @@ -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))