Although the discussion here has already come up with a tail-recursive solution for the binary search tree, it does seem to be possible to do a tail-recursive version for an arbitrary binary tree (or in fact for any tree.) The trick is that our core function will operate on an array of nodes, not on a single one.
Here we do a breadth-first traversal of the tree, stopping when we find the value or when we run out of nodes. Our recursive call is run on the tail of the input array plus the left and right children of the node. (Non-existent or null
nodes are ignored.)
The public function simply wraps the input tree in an array and passes that to our main function.
const _contains = ([t, ...ts], x) =>
t == undefined
? false
: t .val == x
? true
: _contains (ts .concat (t .sx || []) .concat (t .dx || []), x)
const contains = (t, x) =>
_contains ([t], x)
let tree = {val: 10, sx: {val: 5, sx: {val: 13}, dx: {val: 6}}, dx: {val: 32, sx: null, dx: null}};
[10, 12, 13, 32, 42] .forEach (x => console .log (`${x} --> ${contains (tree, x)}`))
I'm loathe to do it, on general principles, but this would definitely be made significantly more efficient if we pushed children onto the array and recurred on an index instead, with something like this:
const push = (x) => (xs) =>
x ? ((xs .push (x)), xs) : xs
const _contains = (ts, i, x) =>
i >= ts .length
? false
: ts [i] .val == x
? true
: _contains (push (ts [i] .dx) (push (ts [i] .sx) (ts)), i + 1, x)
const contains = (t, x) =>
_contains ([t], 0, x)
But the main point is simply that by using an array of nodes, we can prepare for that TCO that never seems to come, unless you're a Safari user.