Skip to content

Commit 98603c7

Browse files
authored
Add utility functions to traverse trees to find minimum, maximum node and the keys adjacent to a node (#13)
* Add utility functions to traverse trees to find minimum, maximum nodes within the tree and within subtrees, and an abovebelow function that returns the nodes immediately above and below a given BinaryNode * updating abovebelow to prevnext to be more generally descriptive. Also accepts the key, functions like an advanced search now. fixed tests using @inferred Nothing. * inlined prevnext with nothing input, and edited comments to meet style. * added @inferred check for BT.prevnext methods. I chose to set AllowedType to the full type (using their subset rule). Let me know if there's a better alternative * switched inferred checks for prevnext to index into results rather than allow supertype of result * removed test typo and added docstrings for new utility functions * Updated docstrings to match style and squashed commits from that process (mybad)
1 parent 5e89b74 commit 98603c7

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

src/binarytree.jl

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,89 @@ function _printkeyvalue(io::IO, node::BinaryNode)
143143
show(ioctx, val)
144144
end
145145
end
146+
147+
# -----------
148+
# UTILITIES
149+
# -----------
150+
151+
"""
152+
BinaryTrees.minnode(tree)
153+
154+
Find the node with the smallest key in the `tree`.
155+
156+
BinaryTrees.minnode(node)
157+
158+
Find the node with the smallest key in the subtree rooted at `node`.
159+
If `nothing` is provided, `nothing` is returned.
160+
"""
161+
minnode(tree::BinaryTree) = minnode(root(tree))
162+
163+
function minnode(node::BinaryNode)
164+
leftnode = left(node)
165+
isnothing(leftnode) ? node : minnode(leftnode)
166+
end
167+
168+
minnode(node::Nothing) = nothing
169+
170+
"""
171+
BinaryTrees.maxnode(tree)
172+
173+
Find the node with the maximum key in the `tree`.
174+
175+
BinaryTrees.maxnode(node)
176+
177+
Find the node with the maximum key in the subtree rooted at `node`.
178+
If `nothing` is provided, `nothing` is returned.
179+
"""
180+
maxnode(tree::BinaryTree) = maxnode(root(tree))
181+
182+
function maxnode(node::BinaryNode)
183+
rightnode = right(node)
184+
isnothing(rightnode) ? node : maxnode(rightnode)
185+
end
186+
187+
maxnode(node::Nothing) = nothing
188+
189+
"""
190+
BinaryTrees.prevnext(tree, k)
191+
192+
Returns a tuple of each node immediately before
193+
and after the `tree` node with key `k`.
194+
195+
If an adjacent node does not exist, `nothing` is returned in its place.
196+
If `k` is `nothing`, returns `(nothing, nothing)`.
197+
"""
198+
function prevnext(tree::BinaryTree, k)
199+
prev, next = nothing, nothing
200+
current = root(tree)
201+
# traverse from the root to the target node, updating candidates
202+
while !isnothing(current) && key(current) != k
203+
if k < key(current)
204+
# current is a potential next (successor)
205+
next = current
206+
current = left(current)
207+
else # k > key(current)
208+
# current is a potential previous (predecessor)
209+
prev = current
210+
current = right(current)
211+
end
212+
end
213+
214+
# if the node wasn't found, return the best candidate values
215+
if isnothing(current)
216+
return (prev, next)
217+
end
218+
219+
# if there is a left subtree, the true previous (predecessor) is the maximum in that subtree
220+
if !isnothing(left(current))
221+
prev = maxnode(left(current))
222+
end
223+
# similarly, if there is a right subtree, the true next (successor) is the minimum in that subtree
224+
if !isnothing(right(current))
225+
next = minnode(right(current))
226+
end
227+
228+
(prev, next)
229+
end
230+
231+
prevnext(tree::BinaryTree, k::Nothing) = (nothing, nothing)

test/runtests.jl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ const BT = BinaryTrees
9393
BT.insert!(tree, 2, 20)
9494
BT.insert!(tree, 1, 10)
9595
BT.insert!(tree, 3, 30)
96-
# deleting a key that does not exist
96+
# deleting a key that does not exist
9797
# does not change the tree
9898
BT.delete!(tree, 4)
9999
@test tree === tree
@@ -219,11 +219,32 @@ const BT = BinaryTrees
219219
@test BT.value(BT.search(tree, (0, 0, 1))) == 1
220220
@test BT.value(BT.search(tree, (1, 0, 0))) == 3
221221

222+
# traversal algorithms
223+
tree = AVLTree{Int,Float64}()
224+
BT.insert!(tree, 0, 5)
225+
BT.insert!(tree, 1, 6)
226+
BT.insert!(tree, 2, 8)
227+
BT.insert!(tree, 3, 10)
228+
BT.insert!(tree, 4, 20)
229+
BT.insert!(tree, 5, 30)
230+
BT.insert!(tree, 6, 40)
231+
@test BT.key(BT.minnode(tree)) == 0
232+
@test BT.key(BT.maxnode(tree)) == 6
233+
@test BT.prevnext(tree, 0)[1] == nothing
234+
@test BT.key.(BT.prevnext(tree, 2)) == (1, 3)
235+
@test BT.key.(BT.prevnext(tree, 5)) == (4, 6)
236+
@test BT.prevnext(tree, nothing) == (nothing, nothing)
237+
222238
# type stability
223239
tree = AVLTree{Int,Int}()
224240
@inferred BT.insert!(tree, 2, 20)
225241
@inferred BT.insert!(tree, 1, 10)
226242
@inferred BT.insert!(tree, 3, 30)
243+
@inferred Nothing BT.minnode(tree)
244+
@inferred Nothing BT.maxnode(tree)
245+
@inferred Nothing BT.prevnext(tree, 2)[1]
246+
@inferred Nothing BT.prevnext(tree, 2)[2]
247+
@inferred BT.prevnext(tree, nothing)
227248
@inferred Nothing BT.search(tree, 2)
228249
@inferred Nothing BT.search(tree, 1)
229250
@inferred Nothing BT.search(tree, 3)
@@ -234,6 +255,11 @@ const BT = BinaryTrees
234255
@inferred BT.insert!(tree, 2)
235256
@inferred BT.insert!(tree, 1)
236257
@inferred BT.insert!(tree, 3)
258+
@inferred Nothing BT.minnode(tree)
259+
@inferred Nothing BT.maxnode(tree)
260+
@inferred Nothing BT.prevnext(tree, 2)[1]
261+
@inferred Nothing BT.prevnext(tree, 2)[2]
262+
@inferred BT.prevnext(tree, nothing)
237263
@inferred Nothing BT.search(tree, 2)
238264
@inferred Nothing BT.search(tree, 1)
239265
@inferred Nothing BT.search(tree, 3)
@@ -244,6 +270,11 @@ const BT = BinaryTrees
244270
@inferred BT.insert!(tree, "key2", 2)
245271
@inferred BT.insert!(tree, "key1", 1)
246272
@inferred BT.insert!(tree, "key3", 3)
273+
@inferred Nothing BT.minnode(tree)
274+
@inferred Nothing BT.maxnode(tree)
275+
@inferred Nothing BT.prevnext(tree, "key2")[1]
276+
@inferred Nothing BT.prevnext(tree, "key2")[2]
277+
@inferred BT.prevnext(tree, nothing)
247278
@inferred Nothing BT.search(tree, "key2")
248279
@inferred Nothing BT.search(tree, "key1")
249280
@inferred Nothing BT.search(tree, "key3")
@@ -254,6 +285,11 @@ const BT = BinaryTrees
254285
@inferred BT.insert!(tree, (0, 1, 0), 2)
255286
@inferred BT.insert!(tree, (0, 0, 1), 1)
256287
@inferred BT.insert!(tree, (1, 0, 0), 3)
288+
@inferred Nothing BT.minnode(tree)
289+
@inferred Nothing BT.maxnode(tree)
290+
@inferred Nothing BT.prevnext(tree, (0, 1, 0))[1]
291+
@inferred Nothing BT.prevnext(tree, (0, 1, 0))[2]
292+
@inferred BT.prevnext(tree, nothing)
257293
@inferred Nothing BT.search(tree, (0, 1, 0))
258294
@inferred Nothing BT.search(tree, (0, 0, 1))
259295
@inferred Nothing BT.search(tree, (1, 0, 0))

0 commit comments

Comments
 (0)