Higher order functions in tree traversal - python

How would I be able to create common tree operations such as insert and search while passing in functions to reduce redundancy. For example, a recursive function calls itself on the left branch when the value passed in is greater than the current node. If I were able to pass in functions like insert and search, I'd be able to factor out a lot of the traversal. The main problem area I'm seeing is that both functions have different base cases. Example solutions in python would be appreciated.
def insert(n, node = root):
if node == None:
node.data = n
node.left, node.right, node.middle = None
elif node.data == n:
insert(node.middle)
elif node.data < n:
insert(right)
else:
insert(left)
def search(n, node = root):
if node == None:
return false
elif node.data == n:
return true
elif node.data < n:
search(right)
else:
search(left)

Your logic for inserting is not correct. You are trying to set attributes on None.
For reusing common code you can use function decorators. Implement common code for traversing tree in decorator function and action on found element in operation function. You can change your code as follow:
def tree_operation(f):
def recursive_wrapper(n, node):
if node == None or node.data == n:
# tree traversed to final point. do action for found element or
# None
return f(n, node)
# try getting closer to interesting element
elif node.data < n:
return recursive_wrapper(n, node.right)
else:
return recursive_wrapper(n, node.left)
return recursive_wrapper
#tree_operation
def search(n, node):
if node == None:
return False
elif node.data == n:
return True
#tree_operation
def insert(n, node):
if node == None:
# this obviously fail
node.data = n
node.left, node.right, node.middle = None
elif node.data == n:
insert(node.middle)
In fact it passes functions, as you noted in question plus renames resultant function to passed in function. Above decorator syntax for insert function does this:
insert = tree_operation(insert)

I think its better to not combine the iterative part of functions in a single one because it complicates the code. But if you think that the iterative part is complex and you need to write it once, you can re-factor it as follow:
def do_iterate(base_function, n, node=root):
if node == None or node.data == n:
base_function(n, node)
elif node.data < n:
do_iterate(base_function, n, node.right)
else:
do_iterate(base_function, n, node.left)
Then, you can write your own base_function that will be called when the base conditions met. For example you can use base_insert() and base_search() functions in place of insert() and search() functions.
def base_insert(n, node):
if node == None:
node.data = n
node.left, node.right, node.middle = None
else:
do_iterate(base_insert, n, node.middle)
def base_search(n, node):
if node == None:
return false
else:
return true
So you can use your algorithms like below:
do_iterate(base_insert, 7)
do_iterate(base_search, 4)
At the end, I'm not sure it's better than your simple code.

Related

My solution for LeetCode problem 235 works well in local but is rejected by LeetCode

I'm solving the LeetCode problem 235. Lowest Common Ancestor of a Binary Search Tree:
Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given nodes in the BST.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
My solution I ran in local interpreter is below.
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
root = TreeNode(6)
root.left = TreeNode(2)
root.right = TreeNode(8)
root.left.left = TreeNode(0)
root.left.right = TreeNode(4)
root.left.right.left = TreeNode(3)
root.left.right.right = TreeNode(5)
root.right.left = TreeNode(7)
root.right.right = TreeNode(9)
p = 2
q = 8
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
def pathFind(path, node, target):
path.append(node)
if node.val == target:
return path
elif node.val < target:
return pathFind(path, node.right, target)
elif node.val > target:
return pathFind(path, node.left, target)
else:
return None
path_p = pathFind([], root, p)
path_q = pathFind([], root, q)
idx = 0
while True:
if path_p[idx] == path_q[idx]:
idx += 1
else:
break
return path_p[idx - 1]
print(Solution().lowestCommonAncestor(root, p, q))
My code works well for many test cases but, in submission in LeetCode, it does not pass even the base case that I passed in my local interpreter.
For example, when I set p = 2 and q = 8 (base case), with the sample BST I constructed in the code above, LeetCode rejects my solution.
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
def pathFind(path, node, target):
path.append(node)
if node.val == target:
return path
elif node.val < target:
return pathFind(path, node.right, target)
elif node.val > target:
return pathFind(path, node.left, target)
else:
return None
path_p = pathFind([], root, p)
path_q = pathFind([], root, q)
idx = 0
while True:
if path_p[idx] == path_q[idx]:
idx += 1
else:
break
return path_p[idx - 1]
What am I missing here?
There are these issues:
A misunderstanding: the parameters p and q are not values, but nodes. The code challenge description clearly states "two given nodes". So when calling pathFind make sure to pass p.val and q.val as last argument.
When p happens to be an ancestor of q (or vice versa), the final loop will not find a node that is different, but run in a "list index out of range" error. There needs to be a check whether the index is still valid in both lists, if not, return the previous node. One way to achieve this is to use zip(path_p, path_q) as that will only visit pairs.
Corrected code:
class Solution:
def lowestCommonAncestor(self, root, p, q):
def pathFind(path, node, target):
path.append(node)
if node.val == target:
return path
elif node.val < target:
return pathFind(path, node.right, target)
elif node.val > target:
return pathFind(path, node.left, target)
else:
return None
path_p = pathFind([], root, p.val)
path_q = pathFind([], root, q.val)
for idx, (a, b) in enumerate(zip(path_p, path_q)):
if a != b:
return path_p[idx - 1]
return path_p[idx]

Pytest testing for class value

Doing a basic implementation of a binary tree in python and testing the implementation using pytest. One of the unit test is to check if the new node is inserted properly to the left (or the right) of the parent node. When I do the assert statement is shows the correct comparison on the left and on the right of the == but tails me the test failed. Snippets of code is attached.
Relevant part of the main code:
class Node:
# Contains data variables.
def __init__(self, val: Optional[int]=None) -> None:
self.value: Optional[int] = val
self.left: Optional[Node] = None
self.right: Optional[Node] = None
def __repr__(self):
return f"{self.value}"
def insert(self, data: int) -> bool:
# Insert a node to the right or left of a parent node.
# Return False only if trying to insert duplicate.
if self.value == data: # Duplicate found
return False
if self.value > data: # New node to the left of parent node
if self.left: # If left exist, then recurse
return self.left.insert(data)
self.left = Node(data) # Default, create a new left node
return True
else:
if self.right: # If right exist, then recurse
return self.right.insert(data)
self.right = Node(data) # Default, create a new right node
return True
Specific pytest unit test
def test_node_insert_left():
node = Node(4)
node.insert(3)
#print(f"\n{node.left}-{node}-{node.right}")
assert node.value == 4
assert node.left == Node(3) # I've also tried node.left == 3
assert type(node.left) == type(Node())
assert node.right == None
The error message
> assert node.left == Node(3)
E assert 3 == 3
E -3
E +3
Two nodes will not equal unless you implement __eq__ in your node class.
You could write an __eq__ method in Node:
def __eq__(self, other):
if not isinstance(other, Node):
return NotImplemented
return self.value==other.value
if you want to regard any two nodes holding the same value as equal.
Then assert node.left == Node(3) would work.
However, sinces nodes contain more than just a value, that might not be ideal.
More simply:
For your assertion, what you actually want to test is that the node holds the value 3. For that, you could simply change your assertion to
assert node.left.value == 3

Error on checking if a tree is a Binary Search Tree

I am currently trying to check if a tree is a BST, while keeping notice of the fact that the values must not be equal to any other one in the tree. I tried keeping count of the interval on which each value should be ( considering a min and a max as arg[0] and arg[1]).
If we are for example going all the way down on the left subtree, there will be no min, only a max. However, when we switch to the right, we will also have a minimum ( the value of the root node we just switched right from).
However, my code is not showing the right answer and i have no idea why. Could you please help me?
These are my functions: ( i am resolving this on hackerrank therefore that's why i have two functions instead of one)
""" Node is defined as
class node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
"""
def check_binary_search_tree_(root):
check_bst(root,None,None)
def check_bst(root,*arg):
res, res2 = True, True
if arg[0] is None and arg[1] is not None:
if root.data >=arg[1]:
return False
elif arg[1] is None and arg[0] is not None:
if root.data <= arg[0]:
return False
elif arg[1] is not None and arg[0] is not None and (root.data<=arg[0] or root.data >= arg[1]):
return False
if root.left:
res = check_bst(root.left, arg[0], root.data)
if root.right:
res2= check_bst(root.right, root.data, arg[1])
if not res or not res2:
return False
return True
Your problem here is that you don't have the check_binary_search_tree_ function that HackerRank calls returning anything. Instead of this
def check_binary_search_tree_(root):
check_bst(root,None,None)
you should be doing this
def check_binary_search_tree_(root):
return check_bst(root,None,None)

How can I recursively insert the Fibonacci sequence into a binary tree

Hope someone can help, I'm not a programmer, but have been interested in exploring Fibonacci sequence and it's recursive tree...
I've created a Binary Tree class, along with an associated TreeNode class, and want to generate a binary tree of the recursive calls created by:
f(n) = f(n-1) + f(n-2) for a given value of n
I'd want to add it as an InsertFibonacci method of my Binary Tree class, replacing the standard Insert method:
def insertNode(self, root, inputData):
if root == None:
return self.addNode(inputData)
else:
if inputData <= root.nodeData:
root.left = self.insertNode(root.left, inputData)
else:
root.right = self.insertNode(root.right, inputData)
return root
Would I add somekind of decorator to the Fib function?
# Fib function
def f(n):
def helper(n):
left = f(n-1)
right = f(n-2)
return left,right
if n == 0:
return 0
elif n == 1:
return 1
else:
left, right = helper(n)
return left + right
Here's the simplest solution I can think of:
class FibTree(object):
def __init__(self, n):
self.n = n
if n < 2:
self.value = n
else:
self.left = FibTree(n - 1)
self.right = FibTree(n - 2)
self.value = self.left.value + self.right.value
Here's one way:
def insertFibonacci(self, n):
current = self.addNode(n)
if n > 1:
current.left = self.insertFibonacci(n-1)
current.right = self.insertFibonacci(n-2)
# if you want the fibonacci numbers instead of the calls:
# current.value = current.left.value + current.right.value
return current
Assumes positive n.
Should return the root of the fibonacci call tree.
Note that this won't exactly be the same kind of binary tree; it won't satisfy the ordering invariant that a binary search tree does. I'm assuming you just want to use your existing structure for convenience.

Print a binary tree, python, in inorder

Me and my friend are doing some school work with programming in Python 3.1 and are VERY stuck. We're programming a binary tree and it's working fine except when we want to print all the nodes in inorder in a way that would create a sentence (all the words in inorder just after one another in a row). We have been looking all over the internet for clues as to how to procede and we've been working with this little thing for like two hours. Any advice/help would be awesome.
Our program/Binary tree:
class Treenode:
def __init__(self, it = None, le = None, ri = None):
self.item = it
self.left = le
self.right = ri
class Bintree:
def __init__(self):
self.item = None
self.left = None
self.right = None
def put(self, it = None):
key = Treenode(it)
if self.item == None:
self.item = key
return
p = self.item
while True:
if key.item < p.item:
if p.left == None:
p.left = key
return
else:
p = p.left
elif key.item > p.item:
if p.right == None:
p.right = key
return
else:
p = p.right
else:
return
def exists(self, it):
key = it
p = self.item
if p == key:
return True
while True:
if key < p.item:
if p.left == None:
return False
else:
p = p.left
elif key > p.item:
if p.right == None:
return False
else:
p = p.right
else:
return
def isEmpty(self):
if self.item == None:
return True
else:
return False
def printtree (Treenode):
if Treenode.left != None:
printtree (Treenode.left)
print (Treenode.item)
if Treenode.right != None:
printtree (Treenode.right)
We get a sort of print when we run the program which looks like this: "bintree.Treenode object at 0x02774CB0", which is not what we want.
We use the tree by running this:
import bintree
tree = bintree.Bintree()
print(tree.isEmpty()) # should give True
tree.put("solen")
print(tree.isEmpty()) # should give False
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
tree.put("upp")
tree.put("himlarunden")
tree.put("manen")
tree.put("seglar")
tree.put("som")
tree.put("en")
tree.put("svan")
tree.put("uti")
tree.put("midnattsstuden")
print(tree.exists("visa")) # should give False
print(tree.exists("ban")) # should give True
tree.printtree() # print sorted
Also, the second last row gives us "None" instead of "True", which is wierd.
To print a binary tree, if you are printing a leaf you just print the value; otherwise, you print the left child then the right child.
def print_tree(tree):
if tree:
print tree.value
print_tree(tree.left)
print_tree(tree.right)
print(tree.exists("visa")) returns None, because in the last line of exists() there's return statement without any value (which defaults to None).
Also you shouldn't name a printtree argument Treenode since it's a name of an existing class and that might lead to confusion. It should look more like:
def printtree(tree_node):
if tree_node.left is not None:
printtree(tree_node.left)
print(tree_node.item)
if tree_node.right is not None:
printtree(tree_node.right)
Another thing is calling printtree - it's a function, not Bintree method, so I suppose you should call it printtree(tree).
One way to make testing easier is to use -assert()- instead of printing things and then referring back to your code.
tree = Bintree()
assert(tree.isEmpty())
tree.put("solen")
assert(not tree.isEmpty())
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
http://docs.python.org/reference/simple_stmts.html#the-assert-statement
It raises an error if its condition is not true. I know that doesn't fix your bug but making things less ambiguous always helps debugging.
You are not specifying a starting case for printtree(). You're defining how to recurse through your tree correctly, but your call to printtree() has no node to start at. Try setting a default check to see if a parameter is passed in, and if one isn't start at the head node of the bintree.
The reason your second to last line is printing None is because, in your exists method, you just have a "return", rather than a "return True", for the case of finding a `p.item' that is equal to key.

Categories