It seems obvious that a recursion is realized using stacks. But how indeed a general recursion realized using a stack? For example, if we have a custom function that is recursive, can we rewrite the code using just a stack and iteration?
Here is one example (I was giving the wrong example in another post):
def recursiveCall(node):
if node==None:
return (0,True)
(left,lstatus) = recursiveCall(node.left)
(right,rstatus) = recursiveCall(node.right)
if lstatus==True and rstatus==True:
if abs(left-right)<=1:
return (max(left,right)+1,True)
else:
return (0,False)
else:
return (0,False)
or an easier one :
def recursiveInorder(node):
if node==None:
return
recursiveInorder(node.left)
print(node.val)
recursiveInorder(node.right)
how do you realize this recursion using stacks? Note, I am not asking for an iteration solution to the above two examples. I believe there must be. But I suppose those iteration solutions are not trying to reproduce the recursion mechanism using a stack. What I wanted is to see, if ever possible, those recursions can be completely replaced by custom coded stack-mechanism (basically making the implicit recursion mechanism embedded in the compiler or whatsoever explicit) .
I think one needs to find a way to track and restore program status, local variables etc?
thx.
node class is defined as:
class node:
def __init__(self,x):
self.val=x
self.left=None
self.right=None
Basically, when simulating a recursive call, you need to push on the stack local variables and the point where execution should resume after returning.
I'll indicate the relevant points of execution by numbered comments here. Think of them as goto labels.
def recursiveInorder(node):
#0
if node==None:
return
recursiveInorder(node.left)
#1
print(node.val)
recursiveInorder(node.right)
#2
return
Now, we can use an if-elif statement to simulate goto statements:
def in_order(node):
stack = [(None, None)] #sentinel
goto = 0
while stack:
if goto == 0:
if node is None:
#return
(node, goto) = stack.pop()
else:
#push state and recurse left
stack.append((node, goto+1))
(node, goto) = (node.left, 0)
elif goto == 1:
print(node.val)
#push state and recurse right
stack.append((node, goto+1))
(node, goto) = (node.right, 0)
else:
#return
(node, goto) = stack.pop()
In the end, (None, None) will be popped but the values are never used because the while loop ends.
The above code is the result of a straightforward conversion. Next, we can apply various optimizations to simplify it.
The last else branch is not doing useful work. We can remove it if we also remove the push that would take us there.
def in_order(node):
stack = [(None, None)] #sentinel
goto = 0
while stack:
if goto == 0:
if node is None:
#return
(node, goto) = stack.pop()
else:
#push state and recurse left
stack.append((node, goto+1))
(node, goto) = (node.left, 0)
else:
print(node.val)
#recurse right
(node, goto) = (node.right, 0)
Now the goto value that is pushed the stack is always 1. We only need to push node on the stack and assign goto = 1 when popping from stack.
def in_order(node):
stack = [None] #sentinel
goto = 0
while stack:
if goto == 0:
if node is None:
#return
(node, goto) = (stack.pop(), 1)
else:
#push state and recurse left
stack.append(node)
(node, goto) = (node.left, 0)
else:
print(node.val)
#recurse right
(node, goto) = (node.right, 0)
If we change the inner if into a while loop...
def in_order(node):
stack = [None] #sentinel
goto = 0
while stack:
if goto == 0:
while node is not None:
#push state and recurse left
stack.append(node)
node = node.left
#return
(node, goto) = (stack.pop(), 1)
else:
print(node.val)
#recurse right
(node, goto) = (node.right, 0)
...we see that after each branch of the if statement, we want to go to the other branch, until in the end we pop the sentinel value. We can eliminate goto and the if statement, if we add a check for empty stack in the middle. If we place the check before the pop, we don't need the sentinel on stack any more.
def in_order(node):
stack = []
while True:
while node is not None:
stack.append(node)
node = node.left
if stack:
node = stack.pop()
print(node.val)
node = node.right
else:
return
Now the code looks clean and simple.
Related
I'm currently working on leetcode problem 366 where we have to find list of lists that contains values of leaves of each generation. I wanted to achieve this by recursion where if a node does not have left or right child, the value is recorded then the node removed by setting it to None. Here is my code:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
leaf_list = []
sub_list = []
def traverse(node):
if node == None:
return
if node.left == None and node.right == None:
sub_list.append(node.val)
node = None
return
traverse(node.left)
traverse(node.right)
return root
while True:
if root == None:
break
sub_list = []
traverse(root)
leaf_list.append(sub_list)
print(leaf_list)
return leaf_list
The problem seems to be that when a certain node is set to None, that change isn't retained. Why is it that I can't set a node to None to remove it?
Thanks
The tree can only be mutated when you assign to one if its node's attributes. An assignment to a variable, only changes what the variable represents. Such assignment never impacts whatever previous value that variable had. Assigning to a variable is like switching from one value to another without affecting any data structure. So you need to adapt your code such that the assignment of None is done to a left or right attribute.
The exception is for the root node itself. When the root is a leaf, then there is no parent to mutate. You will then just discard the tree and switch to an empty one (None).
One way to achieve this, is to use the return value of traverse to update the child-reference (left or right) that the caller of traverse needs to update.
Here is your code with those adaptations:
def findLeaves(root):
sub_list = []
def traverse(node):
if not node:
return
if not node.left and not node.right:
sub_list.append(node.val)
return # By returning None, the parent can remove it
node.left = traverse(node.left) # Assign the returned node reference
node.right = traverse(node.right)
return node # Return the node (parent does not need to remove it)
leaf_list = []
while root:
sub_list = []
root = traverse(root)
leaf_list.append(sub_list)
return leaf_list
I have a btree class and an insert function, to insert nodes to a tree, breadth wise. But the tree isn't inserting nodes to the right.
I'm creating the root node. The insert function inserts left and right nodes to the root correctly.
Then recursively, I try to insert two nodes to the left node and two to the right node. But in this step, all nodes are added to the left only. Nodes get added to a None parent as well.
I know, I'm making a mistake in the last else statement inside insert function. But I've tried many combinations, but all have resulted in some error.
class BinTree(object):
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def insert(self,val):
if self.left is None:
self.left = BinTree(val)
elif self.right is None:
self.right = BinTree(val)
elif self.left:
self.left.insert(val)
else:
self.right.insert(val)
root = BTree('A')
root.insert('B')
root.insert('C')
root.insert(None)
root.insert('D')
root.insert(None)
root.insert('E')
root.insert('F')
Expected:
A
/ \
B C
/\ /\
None D None E
/
F
Getting:
A
/ \
B C
/ \
None D
/ \
None E
/
F
Your code will traverse to the left as soon as it finds a node there that is not None, which is like a depth-first search (DFS). So the code does not look further at the right side to see if there are still some vacancies to be filled there, but goes left anyway. This results in the bias towards the left of your tree.
Instead, you should use a breadth-first search (BFS) to search for the next vacancy in the tree, so in breadth first order. For that you could use a separate method that will perform this BFS and returns the position of the vacancy (by providing its parent node and which side the new child node should be on).
Here is how that new method would look:
def next_free(self):
queue = [self]
while len(queue):
node = queue.pop(0) # Here you get the nodes in BFS order
if node.val is None: # Cannot have children
continue
for side, child in enumerate((node.left, node.right)):
if child is None: # Found the first vacancy in BFS order!
return node, side
queue.append(child)
Now the insert method becomes trivial:
def insert(self,val):
node, side = self.next_free()
if side == 0:
node.left = Node(val)
else:
node.right = Node(val)
You can see it run on repl.it.
Your tree is build exactly as your code suggest.
The insert function checks if there is an empty child and set if it found one - else it is going recursively to the left (regardless of its length) and that is exactly the tree you get.
Second, your output is not clear - what do you mean by adding None?
In order to achieve building of full tree you need to keep count of your elements.
Then we will be able the use the division on the count by 2 to find the right path (taking leaf or right) till reaching the right leaf. Add self.cnt = 1 to the constructor. Using this for pseudocode for insert:
insert:
cnt = self.cnt++ // Increase the count and get the new value
while (cnt > 0) {
path.push(cnt % 2 == 0 ? left : right) // If even, go left. Else go right.
cnt = cnt / 2
}
path.reverse // We need to start from the last element we pushed
current = head
while (path not empty)
current = current.path.pop
current = val
Try to look at the tree number to understand it better:
1
/ \
2 3
/ \ / \
5 6 7 8
You can't really get the result you want with recursion with the current field you save. Each node only "knows" its current state, and that's why the right side of your tree will forever remain at depth 1.
One solution that comes to mind is adding a right children and left children amount field. This will help tracking the balance. It would look like this:
class Node(object):
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.right_count = 0
self.left_count = 0
self.even_depth = True
self.needed_to_even = 1
def insert(self, val):
if self.left is None:
self.left = Node(val)
self.left_count += 1
elif self.right is None:
self.right = Node(val)
self.right_count += 1
elif self.left_count > self.right_count + self.needed_to_even or not self.even_depth:
self.even_depth = False
if self.left_count == self.right_count:
self.needed_to_even *= 2
self.even_depth = True
self.right.insert(val)
self.right_count += 1
else:
self.left.insert(val)
self.left_count += 1
I did recursive procedure for binary tree post order traversal in python. This is the code.
from collections import namedtuple
from sys import stdout
Node = namedtuple('Node', 'data, left, right')
tree = Node(1,
Node(2,
Node(4,
Node(7, None, None),
None),
Node(5, None, None)),
Node(3,
Node(6,
Node(8, None, None),
Node(9, None, None)),
None))
def printwithspace(i):
stdout.write("%i " % i)
def postorder(node, visitor = printwithspace):
if node:
print "%d-->L"%node.data
postorder(node.left, visitor)
print "%d-->R"%node.data
postorder(node.right, visitor)
print "Root--%d"%node.data
else:
print "Null"
stdout.write('\n postorder: ')
postorder(tree)
stdout.write('\n')
Now, I want to do an iterative procedure for binary tree post order traversal in PYTHON. Can anyone help with this?
Thanks in advance.
The following code should work. Basically, you do a depth first search with a stack for the nodes.
Additionally, you have a second stack which parallelly stores whether a node has been expanded already.
def postorder_iteratively(node):
stack = [node]
expanded = [False]
while stack:
while stack and not stack[-1]: #remove "non-existent" nodes from the top
stack = stack[:-1]
expanded = expanded[:-1]
if stack and not expanded[-1]: #expand node
stack += [stack[-1].right, stack[-1].left]
expanded[-1] = True
expanded += [False, False]
elif stack and expanded[-1]: #if node has been expanded already, print it
print stack[-1].data
stack = stack[:-1]
expanded = expanded[:-1]
Here is the code
def postorder_traverse(root):
result = []
list = [] #simply use list to mimic stack
visited_node = None
cur_node = root
while len(list) > 0 or cur_node is not None:
if cur_node is not None:
#remember the middle node by "pushing" it to the stack
list.append(cur_node)
cur_node = cur_node.left
else:
middle_node = list[len(list)-1]
#visit the middle node only if the right node is none
#or the right node is already visited
if middle_node.right is None or visited_node == middle_node.right:
#visit the middle node
result.append(middle_node.data)
visited_node = list.pop(len(list)-1)
else:
#else, move to right
cur_node = middle_node.right
return result
So far I have
def tree_iterate():
parent, current = None, self.root
lst = []
while current is not None:
if current.left not None:
lst.append(current.item)
parent, current = current, current.left
if current.right not None:
lst.append(current.item)
parent, current = current, current.right
(sorry about spacing I'm quite new at this)
I'm not quite sure how to iterate on both sides of the tree when current has left and right, without using recursion. My main goal is to have a list of all the nodes in this BSTenter code here
To get a list of all nodes in the BST iteratively, use Breadth-First Search (BFS). Note that this won't give you the nodes in sorted order:
queue = [root]
result = []
while queue:
l = queue.pop(0)
result.append(l)
if l.left != None:
queue.append(l.left)
if l.right!= None:
queue.append(l.right)
If you want the nodes in sorted order, you will need to simulate inorder traversal using a stack:
result = []
stack = [root]
while stack:
stack[-1].visited = True
if stack[-1].left != None and not stack[-1].left.visited:
stack.append(stack[-1].left)
else:
node = stack.pop()
result.append(node)
if stack[-1].right != None:
stack.append(stack[-1].right)
I have a practice interview question which tells me to verify if a tree is a balanced search tree or not and give a verification method... I have the class as
Class Node:
def __init__(self, k, val):
self.key = k
self.value = val
self.left = None
self.right = None
and other function definitions for the tree max and min values as
def tree_max(node):
maxleft = float('-inf') if not node.left else tree_max(node.left)
maxright = float('-inf') if not node.right else tree_max(node.right)
return max(node.value, maxleft, maxright)
def tree_min(node):
minleft = float('-inf') if not node.right else tree_min(node.left)
minright = float('-inf') if not node.left else tree_min(node.right)
return min(node.value, minleft, minright)
My verification method as
def verify(node):
if tree_max(node.left) <= node.value and node.value <= tree_min(node.right):
if verify(node.left) and verify(node.right):
return True
else:
return False
else:
return False
My problem occurs when I try to implement the verification method I seem to always get false even when I try to make a BST tree. My implementation is as follows:
root= Node(10, "Hello")
root.left = Node(15, "Fifteen")
root.right= Node(30, "Thirty")
print verify(root)
root = Node(10, "Ten")
root.right = Node(20, "Twenty")
root.left = Node(5, "Five")
root.left.right = Node(15, "Fifteen")
print verify(root)
Both are giving me False...Is there a problem with my verification function or my min/max function...Any help would be appreciated.
I see four errors in your code.
First, your check for null children is backwards in tree_min. That is, you're checking if node.right exists before accessing node.left, and vise versa.
Second, tree.min returns negative infinity when called on a leaf node. You need to use positive infinity in the min calculation (negative infinity is correct in the max version).
Third, you have a logic error within verify, as it unconditionally calls tree_min or tree_max and itself on it's child nodes, even if one or both of them are None. I suggest making all the functions handle being passed None, rather than relying on the caller to do the right thing. This also simplifies the min and max code a bit!
Lastly, you're doing your comparisons on node.value, which is the string you're giving each node. I suspect you want to be comparing using node.key instead. Comparing a float (like float("-inf")) to a string (like "ten") is an error in Python 3, and even in Python 2 where it is legal, it probably doesn't work like you would expect.
With those issues fixed, I get expected results when I create valid and invalid trees. Your two examples are both invalid though, so if you were using them to test, you will always get a False result.
Finally, a couple of minor style issues (that aren't bugs, but still things that could be improved). Python supports chained comparisons, so you can simplify your first if statement in verify to tree_max(node.left) <= node.key <= tree_min(node.right). You can further simplify that part of the code by connecting the checks with and rather than nesting an additional if statement.
Here's a version of your code that works for me (using Python 3, though I think it is all backwards compatible to Python 2):
class Node:
def __init__(self, k, val):
self.key = k
self.value = val
self.left = None
self.right = None
def tree_max(node):
if not node:
return float("-inf")
maxleft = tree_max(node.left)
maxright = tree_max(node.right)
return max(node.key, maxleft, maxright)
def tree_min(node):
if not node:
return float("inf")
minleft = tree_min(node.left)
minright = tree_min(node.right)
return min(node.key, minleft, minright)
def verify(node):
if not node:
return True
if (tree_max(node.left) <= node.key <= tree_min(node.right) and
verify(node.left) and verify(node.right)):
return True
else:
return False
root= Node(10, "Hello")
root.left = Node(5, "Five")
root.right= Node(30, "Thirty")
print(verify(root)) # prints True, since this tree is valid
root = Node(10, "Ten")
root.right = Node(20, "Twenty")
root.left = Node(5, "Five")
root.left.right = Node(15, "Fifteen")
print(verify(root)) # prints False, since 15 is to the left of 10