I was following along here and I was trying to get some practice into converting normal recursive functions into tail-recursive functions. I managed to understand the fibonacci and factorial versions but this one stumped me. I understand what the algorithm is doing and its the else statement that confused me in the conversion.
Inside the else, it tries to find a number that's closer to what you are looking for before giving up and going with the number it finds that is less than the one you suggest.
I am not sure how to write the helper function that makes this tail recursive. For the fibonacci and factorial, I ended up using an accumulator. Is there something similar that could be used here?
class BSTNode(object):
"""Binary search tree node."""
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
return '(%s, %r, %r)' % (self.val, self.left, self.right)
def find_val_or_next_smallest(bst, x):
"""
Get the greatest value <= x in a binary search tree.
Returns None if no such value can be found.
"""
if bst is None:
return None
elif bst.val == x:
return x
elif bst.val > x:
return find_val_or_next_smallest(bst.left, x)
else:
right_best = find_val_or_next_smallest(bst.right, x)
if right_best is None:
return bst.val
return right_best
I understand that Python doesn't support the tail recursion optimization to allow constant stack space but I was just doing this for practice in Python as I like the syntax
Instead of doing
if right_best is None:
return bst.val
You can pass the best result found so far into the recursive call as an extra argument, and have the recursive call handle this check.
def find_val_or_next_smallest(bst, x, best=None):
"""
Get the greatest value <= x in a binary search tree.
Returns None if no such value can be found.
"""
if bst is None:
return best
elif bst.val == x:
return x
elif bst.val > x:
return find_val_or_next_smallest(bst.left, x, best)
else:
# bst.val is guaranteed to be the best yet, since if we had
# seen a better value higher up, the recursion would have gone
# the other way from that node
return find_val_or_next_smallest(bst.right, x, bst.val)
To Turn the function into tail-recursive you should carry the partial answer with you all the way by adding an extra parameter, val:
def find_val_or_next_smallest(bst, x, val=None):
"""
Get the greatest value <= x in a binary search tree.
Returns None if no such value can be found.
"""
if bst is None:
return val
elif bst.val == x:
val = x
elif bst.val < x and (val is None or bst.val > val):
val = bst.val
if bst.val > x:
return find_val_or_next_smallest(bst.left, x, val)
else:
return find_val_or_next_smallest(bst.right, x, val)
UPDATE
Having a tail recursion means that we can have an iterative solution (vs. recursive), and it can easily be demonstrated on the accepted answer - by converting it into an iterative solution:
def find_val_or_next_smallest(bst, x, val=None):
"""
Get the greatest value <= x in a binary search tree.
Returns None if no such value can be found.
"""
while True:
if bst is None:
return val
elif bst.val == x:
return x
elif bst.val > x:
bst = bst.left
else:
val = bst.val
bst = bst.right
Related
Given a Binary Tree, I wanted to find the depth at which a specific value appears.
def find_depth_of_val(self, root, val):
if not root:
return 10000000
if root.val == val:
return 0
return 1 + min(self.find_node(root.right, val), self.find_node(root.left, val))
This is the idea I had, you'll probably see a wild "return 10000000", which I used to make it so that if there is nothing else on a specific path, i.e. a leaf's next has been reached without finding the node we want, that the function knows the answer isn't there and doesn't return that possibility.
What I'm wondering is whether there's a better way to do this, one without using a random "return 10000000".
Edit: Someone gave me a solution in which I kind of change it to:
def find_depth_of_val(self, root, val):
if not root:
return None
if root.val == val:
return 0
right_side = self.find_node(root.right, val)
left_side = self.find_node(root.left, val)
if right_side is None:
return left_side
elif left_side is None:
return right_side
else:
return 1 + min(self.find_node(root.right, val), self.find_node(root.left, val))
In such a case like this, how should I type hint it considering we could be returning either None or an integer?
That said, I'm still open to seeing if anyone else has any different solution designs!!
This looks weird that find_depth_of_val has both self (a tree) and root (another tree) as parameters. Besides when you state your problem you talk of only one tree and self is actually not used in your method.
So assuming your values in the tree are unique, here is a solution. The method returns None if no path is found or otherwise the depth. Optional[int] means either an int or either None:
def find_depth_of_val(self, val: int) -> Optional[int]:
if self.val == val:
return 0
for node in (self.left, self.right):
if node is not None:
depth = node.find_depth_of_val(val)
if depth is not None:
return depth + 1
return None
I am trying to implement this method, "smaller" for a BST, that returns the values in the tree which are smaller than a given item, in order.
class BinarySearchTree:
def __init__(self, root: Optional[Any]) -> None:
if root is None:
self._root = None
self._left = None
self._right = None
else:
self._root = root
self._left = BinarySearchTree(None)
self._right = BinarySearchTree(None)
def is_empty(self) -> bool:
return self._root is None
def smaller(self, item: Any) -> List:
if self.is_empty():
return []
else:
return self._left.items() + [self._root] + self._right.items()
So far, the "smaller" method will return all of the values in the tree in order, but I'm not sure how to check if those values are smaller and than a given item, and to only return those in a list.
Let's write pseudocode for in-order-tree-walk method which prints the keys of BST in sorted (in-order) order.
in-order-tree-walk(T, x)
if (T != NULL)
in-order-tree-walk(T.left, x)
print T's key
in-order-tree-walk(T.right, x)
smaller method has exactly the same structure as in-order-tree-walk except that it's additional condition which makes it to print keys that are smaller. smaller method's pseudocode will look like
smaller(T, x)
if (T != NULL)
smaller(T.left, x)
if (T's key is less than x)
print T's key
smaller(T.right, x)
We're done. smaller method is now completed. Now let's look at your actual implementation.
Your code prints all keys of BST in sorted order because of the way you implemented it. You have the problem the following part:
def smaller(self, item: Any) -> List:
if self.is_empty():
return []
else:
return self._left.items() + [self._root] + self._right.items()
In return self._left.items() + [self._root] + self._right.items(), you don't check whether the [self.root] is less than item's value or not. You have to check that because you put restraint on printing the key of tree, but in implementation you didn't check it. Since I'm not qualified in Python, I can't complete this part, but I think you've get what the problem is with your code based on above explanations.
I've been playing with BST (binary search tree) and I'm wondering how to do an early exit. Following is the code I've written to find kth smallest. It recursively calls the child node's find_smallest_at_k, stack is just a list passed into the function to add all the elements in inorder. Currently this solution walks all the nodes inorder and then I have to select the kth item from "stack" outside this function.
def find_smallest_at_k(self, k, stack, i):
if self is None:
return i
if (self.left is not None):
i = self.left.find_smallest_at_k(k, stack, i)
print(stack, i)
stack.insert(i, self.data)
i += 1
if i == k:
print(stack[k - 1])
print "Returning"
if (self.right is not None):
i = self.right.find_smallest_at_k(k, stack, i)
return i
It's called like this,
our_stack = []
self.root.find_smallest_at_k(k, our_stack, 0)
return our_stack[k-1]
I'm not sure if it's possible to exit early from that function. If my k is say 1, I don't really have to walk all the nodes then find the first element. It also doesn't feel right to pass list from outside function - feels like passing pointers to a function in C. Could anyone suggest better alternatives than what I've done so far?
Passing list as arguments: Passing the list as argument can be good practice, if you make your function tail-recursive. Otherwise it's pointless. With BST where there are two potential recursive function calls to be done, it's a bit of a tall ask.
Else you can just return the list. I don't see the necessity of variable i. Anyway if you absolutely need to return multiples values, you can always use tuples like this return i, stack and this i, stack = root.find_smallest_at_k(k).
Fast-forwarding: For the fast-forwarding, note the right nodes of a BST parent node are always bigger than the parent. Thus if you descend the tree always on the right children, you'll end up with a growing sequence of values. Thus the first k values of that sequence are necessarily the smallest, so it's pointless to go right k times or more in a sequence.
Even in the middle of you descend you go left at times, it's pointless to go more than k times on the right. The BST properties ensures that if you go right, ALL subsequent numbers below in the hierarchy will be greater than the parent. Thus going right k times or more is useless.
Code: Here is a pseudo-python code quickly made. It's not tested.
def findKSmallest( self, k, rightSteps=0 ):
if rightSteps >= k: #We went right more than k times
return []
leftSmallest = self.left.findKSmallest( k, rightSteps ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k, rightSteps + 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
EDIT The other version, following my comment.
def findKSmallest( self, k ):
if k == 0:
return []
leftSmallest = self.left.findKSmallest( k ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k - 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
Note that if k==1, this is indeed the search of the smallest element. Any move to the right, will immediately returns [], which contributes to nothing.
As said Lærne, you have to care about turning your function into a tail-recursive one; then you may be interested by using a continuation-passing style. Thus your function could be able to call either itself or the "escape" function. I wrote a module called tco for optimizing tail-calls; see https://github.com/baruchel/tco
Hope it can help.
Here is another approach: it doesn't exit recursion early, instead it prevents additional function calls if not needed, which is essentially what you're trying to achieve.
class Node:
def __init__(self, v):
self.v = v
self.left = None
self.right = None
def find_smallest_at_k(root, k):
res = [None]
count = [k]
def helper(root):
if root is None:
return
helper(root.left)
count[0] -= 1
if count[0] == 0:
print("found it!")
res[0] = root
return
if count[0] > 0:
print("visiting right")
find(root.right)
helper(root)
return res[0].v
If you want to exit as soon as earlier possible, then use exit(0).
This will make your task easy!
So I need to find the element in the array that will give the maximum value when inputting to the key. Also, if there is more than one element that does that, the first one must be returned. Furthermore, the key parameter must be optional; if not provided, the function must return the first largest element. So far I've come up with
def recursive_max(seq, key = lambda x: x):
if len(seq) == 1:
return seq[0]
else:
m = recursive_max(max(seq, key), key) .......
I'm quite stuck. I don't fully understand recursion but here are the steps I think I need to take.
1) Get element from the list
2) Input the key into the function
3) Initialize the max
4) Compare across the sequence (which is my array)
I'm quite confused how to write this is in code.
If recursion is the requirement, you can just go
def recursive_max(seq, key=lambda x: x):
if len(seq) == 1:
return seq[0]
else:
return max(seq[0], recursive_max(seq[1:], key), key=key)
Here you compare the first element of the array with the maximum of the rest of the array.
Note that the version above is not tail recursive (although it does not matter in Python --- or, at least, in CPython). A tail-recursive version would look like
def recursive_max2(seq, key=lambda x: x):
if len(seq) == 1:
return seq[0]
else:
return _recursive_max(seq[0], seq[1:], key)
def _recursive_max(x, xs, key):
if len(xs) == 0:
return x
else:
return _recursive_max(max(x, xs[0], key=key), xs[1:], key)
I am trying to solve the following problem:
Return the root of a binary search tree t modified to contain only values <= k. (Using the normal BST class where we have an item,left and right)
def prune(t,k):
if not t:
return None
if k < t.item
while t.item > k:
t = t.left
return t
I think I am doing it completely wrong.. Maybe there is some easy recursive way to do it?
I think you want something like:
def prune(t, k):
if t is None or t.item > k:
return None
t.right = prune(t.right, k)
return t
This is recursive, and will "prune" when it reaches any None node or node larger than k. As this is a BST, t.item <= k means all nodes in t.left will be too, so we can ignore them.