Python: Recursive function for browsing all tree nodes - python

I'm trying to make an recursive function which finds all nodes in a tree. My function, let's name it child(), can find all children of the current node and returns a listĀ“of them.
global nodes
nodes = []
def func():
if len(child(c))==0:
return []
else:
for ch in child(c):
nodes.append(ch)
return func(ch)
It seems it does not work for some reason.
Do you have an idea whats wrong or the problem should be somewhere else in my code?
EDIT: The problem is probably
if len(child(c))==0:
return []
it should check another child instead of return []. But don't know what to put there.

it's because of the return statement inside the for, the function passed only on the first child in the list.
this should work
global nodes
nodes = []
def func(c):
for ch in child(c):
nodes.append(ch)
func(ch)

Related

How to understand recursion in Binary Tree

I have started to learn Recursion , I seem to understand the concept , but at the same time I feel completely lost.
For eg, I was trying to solve find the ancestors of a given node in Binary Tree
My Tree:
1
/ \
7 9
/ \ / \
6 5 2 3
My code :
def findAns(node,target,ans):
if node is None:
return ans
ans.append(node.data)
if node == target:
return ans[:-1] #return the list except the last item(whch will be the target node)
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1] #Delete last node while backtracking
return ans
ans=[]
findAns(root,target,ans) #target node is 5
print(ans)
print(ans[:-1])
OUTPUT :
[1,7,5]
[1, 7]
I am unable to understand the below questions,
When backtracking is completed for the entire tree, list 'ans' will be empty # root position, how come I am able to get the value [1,7,5] in my list ?
when the if condition is satisfied , I return ans[:-1] , because I should not include the target node, but how come when I print 'ans' I am getting the target node as well ?
In the same 'if condition' , if I return ans instead of ans[:-1], y code doesn't work, I get back an empty 'ans' list, why is that ?
But to avoid the confusion I changed my code as mentioned below, I used a global variable . However I don't think it is efficient , any resource or material or explanations for my above question would be of great help to me. Thanks !
Code with Global variable:
_ans=[]
def findAns(node,target,ans):
global _ans
if node is None:
return ans
ans.append(node.data)
if node == target:
_ans.append(list(ans[:-1]))
#return ans[:-1]
ans = findAns(node.left,target,ans)
ans = findAns(node.right,target,ans)
del ans[-1]
return ans
ans=[]
findAns(root,target,ans)
_ans[0]
You expected the root after backtracking returns empty list, well it does. The problem is the function returns the empty list but you don't catch it:
res=findAns(root, target, ans)
print(res)
Output:
[]
From question 1 you may still think printing ans will be the same as catching the return value, but when you return ans[:-1] you have already lost the original list you passed in. This is heavily related to list in python being mutable and list[:-1] will return a completely new list and modifying the new list won't affect the ans you first passed in. So after it returns a new list, the backtracking ans = findAns(root.right, target, ans) takes a new list, after that changing the 'new' ans won't change the 'old' ans(the one used to print)
If you change to return ans then it will print out empty list. The problem is every time you iterate through a step in the recursion you append a new node but at the same time you delete one last node, which results in add one and delete one being zero.

Trying to implement a rebalance method in binary search tree, having issues getting the middle of a lyst inside of a method

def inorder(self):
""" inorder """
return self.inorder_helper(self.root)
def inorder_helper(self, cur_node):
""" inorder helper """
RecursionCounter()
lyst = []
if cur_node is not None:
lyst += self.inorder_helper(cur_node.left_child)
lyst.append(str(cur_node.data))
lyst += self.inorder_helper(cur_node.right_child)
final_lyst = list(map(int, lyst))
return final_lyst
def rebalance_tree(self):
newlyst = inorder(self)
newroot = len(newlyst-1) / 2
midderoot = newlyst[newroot]
print(newroot)
bst = BinarySearchTree()
bst.add(5)
bst.add(10)
bst.add(12)
bst.add(20)
bst.rebalance_tree()
So i have this binary search tree, and i am trying to add a method to make it a AVL tree, and have a rebalance tree method.
So the first thing i need to do is take the middle value of the inorder lyst and make it into the root for the rebalance tree.
However, this is not working correctly. If you take a look at the code, i tried to get a newlyst and make it equal to the method inorder, which returns a list of inorder.
I then tried to take the length and divide it by 2, essentially getting the middle value, and then printing it.
However i am getting a "inorder" is not defined error. how do i get the lyst from inorder inside of rebalance_tree? I need the rebalance tree to also have no parameters, but i could have helper functions.

Root to leaf algo bug

Wrote an unnecessarily complex solution to the following question:
Given a binary tree and a sum, determine if the tree has a
root-to-leaf path such that adding up all the values along the path
equals the given sum.
Anyway, I'm trying to debug what went wrong here. I used a named tuple so that I can track both whether the number has been found and the running sum, but it looks like running sum is never incremented beyond zero. At any given leaf node, though, the running sum will be the leaf node's value, and in the next iteration the running sum should be incremented by the current running sum. Anyone know what's wrong with my "recursive leap of faith" here?
def root_to_leaf(target_sum, tree):
NodeData = collections.namedtuple('NodeData', ['running_sum', 'num_found'])
def root_to_leaf_helper(node, node_data):
if not node:
return NodeData(0, False)
if node_data.num_found:
return NodeData(target_sum, True)
left_check = root_to_leaf_helper(node.left, node_data)
if left_check.num_found:
return NodeData(target_sum, True)
right_check = root_to_leaf_helper(node.right, node_data)
if right_check.num_found:
return NodeData(target_sum, True)
new_running_sum = node.val + node_data.running_sum
return NodeData(new_running_sum, new_running_sum == target_sum)
return root_to_leaf_helper(tree, NodeData(0, False)).num_found
EDIT: I realize this is actually just checking if any path (not ending at leaf) has the correct value, but my question still stands on understanding why running sum isn't properly incremented.
I think you need to think clearly about whether information is flowing down the tree (from root to leaf) or up the tree (from leaf to root). It looks like the node_data argument to root_to_leaf_helper is initialized at the top of the tree, the root, and then passed down through each node via recursive calls. That's fine, but as far as I can tell, it's never changed on the way down the tree. It's just passed along untouched. Therefore the first check, for node_data.num_found, will always be false.
Even worse, since node_data is always the same ((0, False)) on the way down the tree, the following line that tries to add the current node's value to a running sum:
new_running_sum = node.val + node_data.running_sum
will always be adding node.val to 0, since node_data.running_sum is always 0.
Hopefully this is clear, I realize that it's a little difficult to explain.
But trying to think very clearly about information flowing down the tree (in the arguments to recursive calls) vs information flowing up the tree (in the return value from the recursive calls) will make this a little bit more clear.
You can keep a running list of the path as part of the signature of the recursive function/method, and call the function/method on both the right and left nodes using a generator. The generator will enable you to find the paths extending from the starting nodes. For simplicity, I have implemented the solution as a class method:
class Tree:
def __init__(self, *args):
self.__dict__ = dict(zip(['value', 'left', 'right'], args))
def get_sums(self, current = []):
if self.left is None:
yield current + [self.value]
else:
yield from self.left.get_sums(current+[self.value])
if self.right is None:
yield current+[self.value]
else:
yield from self.right.get_sums(current+[self.value])
tree = Tree(4, Tree(10, Tree(4, Tree(7, None, None), Tree(6, None, None)), Tree(12, None, None)), Tree(5, Tree(6, None, Tree(11, None, None)), None))
paths = list(tree.get_sums())
new_paths = [a for i, a in enumerate(paths) if a not in paths[:i]]
final_path = [i for i in paths if sum(i) == 15]
Output:
[[4, 5, 6]]

Breaking out of the recursion in multi nested tree python

I have a multi nested tree structure and i am trying to find the current level of the provided string/node.I an using recursion to traverse the nodes and return the current level.
def dicq(self,value,current_level):
d = {}
for child in self.children:
if child.name == value:
print current_level
else:
(child.dicq(value,current_level+1))
return current_level
root.dicq('7.3',1)
root is the nested tree structure and i am giving it 7.3(The node which i am finding the level of) and 1(the default level meaning first children). If i simply print the current_level in the if statement it is correct but if i put return in the if statement it doesn't return.I want to return the current level as soon as i find the node.Any suggestions?
Right now your code is returning current_level irrespective of wheather node you are looking for is found or not.
What you need to do is to return current_level only if matching node is found. If not, return something which indicates matching node is not found. Also, you need to ensure your results propagates properly across levels of recursion and you stop looking further when a match is found.
Here is code which might help. Note: I have not tested it.
def dicq(self,value,current_level):
d = {}
retval = -1
for child in self.children:
if child.name == value:
retval = current_level
else:
retval = child.dicq(value,current_level+1)
if retval != -1:
break
return retval
If value is found, that level will be returned. If not, -1 will be returned indicating that its not found anywhere in that part of the tree.

returns the preorder list of a tree in python

I'm new to python and am trying to write a function that will return the preorder list of a tree recursively. I can get it to get the preorder list, however, it comes with a whole lot of unwanted empty lists from the recursive interaction with the base case.
The code is:
def BinPreOrder(T):
if Is_EmptyBinTree(T):
return []
else:
return BinRoot(T), BinPreOrder(Left(T)), BinPreOrder(Right(T))
How can I get an output of a list of just the values of every node (no empty lists or the like)?
Thanks so much,
I assume that you want a flat list, not a tuple of tuples as your answer. The first problem is that
BinRoot(T), BinPreOrder(Left(T)), BinPreOrder(Right(T))
is a tuple with three elements, not a list. The recursive calls will also return tuples, so you will end a tuple of tuples.
The second problem is all the occurrences of None in your result. As several people have pointed out already, python functions always return None by default, so you have to explicitly avoid the None's.
Try something like this:
def BinPreOrder(T):
if not Is_EmptyBinTree(T):
answer = [BinRoot(T)]
left = BinPreOrder(Left(T))
if left is not None:
answer.extend(left)
right = BinPreOrder(Right(T))
if right is not None:
answer.extend(right)
return answer
EDIT: There's a shorter, clearer way to do this. Instead of letting it return None for empty subtrees, make it return an empty list. Then you can just concatenate the three lists. I was focussing on addressing the problems with your code, instead of thinking about the problem itself. Sorry
def BinPreOrder(T):
if Is_EmptyBinTree(T): return []
return [BinRoot(T)] + BinPreOrder(Left(T)) + BinPreOrder(Right(T))
Just use the not operator to reverse the result from your Is_EmptyBinTree(T):
def BinPreOrder(T):
if not Is_EmptyBinTree(T):
return BinRoot(T), BinPreOrder(Left(T)), BinPreOrder(Right(T))
All functions return None if no other value is explicitly returned.
Make a list and return recursively.
def BinPreOrder(T):
ans=[]
if T is not None:
ans.append(T.val)
ans.extend(BinPreOrder(T.lelt))
ans.extend(BinPreOrder(T.right))
return ans

Categories

Resources