Update parent pointers in a binary tree - python

The question is, given a binary tree, where each node has four pieces of data: left, right ,data and an empty pointer parent, we have to update the tree such that the parent pointer of each node points to its parent (the root parent pointer will naturally point to a NULL value). Now how do I do this? I tried a post-order traversal like this:
last = None
def mypostorder(root):
if root:
mypostorder(root.left)
mypostorder(root.right)
if last:
last.parent = root
last = root
But obviously it is not working, and I know why, after updating the parent pointer of a left-child, it sets it as last, so next time when it visits the right-child (its sibling), it sets its parent to the left-child. How to tweak it to get the correct result? Is it possible to do it iteratively as well, using a stack, may be?

void setParent(node * ptr,node * parent_ptr)
{
if(ptr==NULL)
return;
ptr->parent=parent_ptr; //update the parent for the current node
parent_ptr=ptr; //now update the parent pointer
setParent(ptr->left,parent_ptr); //repeat the process for children
setParent(ptr->right,parent_ptr);
}
Initial Call : setParent(root,NULL);

You have it going the wrong way. I think your solution would make the parent of a node point to its child
Give this a try:
def myPostOrder(root, par=None):
if root:
root.parent = par
myPostOrder(root.left, root)
myPostOrder(root.right, root)

Related

Recursive loop not having expected behavior

So I create a tree in python. I am trying to change some value of every child of the root node. But, every node in my tree is not being hit.
class Node(object):
def __init__(self, value, priority):
self.parent = None
self.children = []
self.value = value
self.priority = priority
def add_child(self, obj):
self.children.insert(obj)
obj.parent = self
def getChildren(self):
return self.children.getAll()
tom = Node("DD",1)
tom.add_child(Node("a",0.3))
tom.add_child (Node("b", 0.6))
tom.getChildren()[0].add_child(Node("c",1))
tom.getChildren()[1].add_child(Node("d",1))
#print(tom.popHighestValue().value)
def getAll(currentNode):
print(currentNode.value)
if(currentNode.getChildren != []):
for sibling in currentNode.getChildren():
sibling.priority = 1
return getAll(sibling)
The tree should look like:
DD
/\
a b
/
c
But only DD->a->c are being hit. I thought the for loop state would be saved and continued after DD -> c was traversed.
The goal is that every node in the tree is hit. And the priority value is set to 1.
A return statement always exits the current function. If you're in a loop when you do this, the rest of the loop is never executed.
If you need to return all the values of the recursive calls, you need to collect them in a list during the loop, then return that list after the loop is done.
But in this case there doesn't seem to be anything you need to return. This function is just for setting an attribute, there's nothing being extracted. So just make the recursive call without returning.
def getAll(currentNode):
print(currentNode.value)
for sibling in currentNode.getChildren():
sibling.priority = 1
getAll(sibling)
BTW, this won't set the priority of the root node, since it only sets the priority of children. If you want to include the root node, it should be:
def getAll(currentNode):
print(currentNode.value)
currentNode.priority = 1
for sibling in currentNode.getChildren():
getAll(sibling)
Also, you shouldn't call getAll() in the getChildren() method. It just return self.children, not self.children.getAll().
If you remove the return before calling getAll() and place it outside the enclosing for loop, that would fix your problem.
In your code, you are unable to process all the children because right after your first iteration you call getAll() with the return statement. So, all the other siblings except first are/will not be explored at every depth.

Implementing a (modified) DFS in a Graph

I have implemented a simple graph data structure in Python with the following structure below. The code is here just to clarify what the functions/variables mean, but they are pretty self-explanatory so you can skip reading it.
class Node:
def __init__(self, label):
self.out_edges = []
self.label = label
self.is_goal = False
self.is_visited = False
def add_edge(self, node, weight):
self.out_edges.append(Edge(node, weight))
def visit(self):
self.is_visited = True
class Edge:
def __init__(self, node, weight):
self.node = node
self.weight = weight
def to(self):
return self.node
class Graph:
def __init__(self):
self.nodes = []
def add_node(self, label):
self.nodes.append(Node(label))
def visit_nodes(self):
for node in self.nodes:
node.is_visited = True
Now I am trying to implement a depth-first search which starts from a given node v, and returns a path (in list form) to a goal node. By goal node, I mean a node with the attribute is_goal set to true. If a path exists, and a goal node is found, the string ':-)' is added to the list. Otherwise, the function just performs a DFS and goes as far as it can go. (I do this here just to easily check whether a path exists or not).
This is my implementation:
def dfs(G, v):
path = [] # path is empty so far
v.visit() # mark the node as visited
path.append(v.label) # add to path
if v.is_goal: # if v is a goal node
path.append(':-)') # indicate a path is reached
G.visit_nodes() # set all remaining nodes to visited
else:
for edge in v.out_edges: # for each out_edge of the starting node
if not edge.to().is_visited: # if the node pointed to is not visited
path += dfs(G, edge.to()) # return the path + dfs starting from that node
return path
Now the problem is, I have to set all the nodes to visited (line 9, visit_nodes()) for the algorithm to end once a goal node is reached. In effect, this sort of breaks out of the awaiting recursive calls since it ensures no other nodes are added to the path. My question is:
Is there a cleaner/better way to do this?
The solution seems a bit kludgy. I'd appreciate any help.
It would be better not to clutter the graph structure with visited information, as that really is context-sensitive information linked to a search algorithm, not with the graph itself. You can use a separate set instead.
Secondly, you have a bug in the code, as you keep adding to the path variable, even if your recursive call did not find the target node. So your path will even have nodes in sequence that have no edge between them, but are (close or remote) siblings/cousins.
Instead you should only return a path when you found the target node, and then after making the recursive call you should test that condition to determine whether to prefix that path with the current edge node you are trying with.
There is in fact no need to keep a path variable, as per recursion level you are only looking for one node to be added to a path you get from the recursive call. It is not necessary to store that one node in a list. Just a simple variable will do.
Here is the suggested code (not tested):
def dfs(G, v):
visited = set() # keep visited information away from graph
def _dfs(v):
visited.add(v) # mark the node as visited
if v.is_goal:
return [':-)'] # return end point of path
for edge in v.out_edges:
neighbor = edge.to() # to avoid calling to() several times
if neighbor not in visited:
result = _dfs(neighbor)
if result: # only when successful
# we only need 1 success: add current neighbor and exit
return [neighbor.label] + result
# otherwise, nothing should change to any path: continue
# don't return anything in case of failure
# call nested function: the visited and Graph variables are shared
return _dfs(v)
Remark
For the same reason as for visited, it is maybe better to remove the is_goal marking from the graph as well, and pass that target node as an additional argument to the dfs function.
It would also be nice to give a default value for the weight argument, so that you can use this code for unweighted graphs as well.
See how it runs on a sample graph with 5 nodes on repl.it.

How to delete all nodes of a Binary Search Tree

I am trying to write a code to delete all nodes of a BST (each node has only three attributes, left, right and data, there are no parent pointers). The following code is what I have come up with, it deletes only the right half of the tree, keeping the left half intact. How do I modify it so that the left half is deleted as well (so that ultimately I am left with only the root node which has neither left or right subtrees)?
def delete(root):
global last
if root:
delete(root.left)
delete(root.right)
if not (root.left or root.right):
last = root
elif root.left == last:
root.left = None
else:
root.right = None
And secondly, can anybody suggest an iterative approach as well, using stack or other related data structure?
Blckknght is right about garbage collection, but in case you want to do some more complex cleanup than your example suggests or understand why your code didn't work, i'll provide an additional answer:
Your problem seems to be the elif node.left == last check.
I'm not sure what your last variable is used for or what the logic is behind it.
But the problem is that node.left is almost never equal to last (you only assign a node to the last variable if both children are already set to None, which they aren't for any of the interesting nodes (those that have children)).
If you look at your code, you'll see that in that if node.left isn't equal to last only the right child gets set to None, and thus only the right part of the subtree is deleted.
I don't know python, but this should work:
def delete(node):
if node:
# recurse: visit all nodes in the two subtrees
delete(node.left)
delete(node.right)
# after both subtrees have been visited, set pointers of this node to None
node.left = None
node.right = None
(I took the liberty of renaming your root parameter to node, since the node given to the function doesn't have to be the root-node of the tree.)
If you want to delete both subtrees, there's no need to recurse. Just set root.left and root.right to None and let the garbage collector take care of them. Indeed, rather than making a delete function in the first place, you could just set root = None and be done with it!
Edit: If you need to run cleanup code on the data values, you might want to recurse through the tree to get to all of them if the GC doesn't do enough. Tearing down the links in the tree shouldn't really be necessary, but I'll do that too for good measure:
def delete(node):
if node:
node.data.cleanup() # run data value cleanup code
delete(node.left) # recurse
delete(node.right)
node.data = None # clear pointers (not really necessary)
node.left = None
none.right = None
You had also asked about an iterative approach to traversing the tree, which is a little more complicated. Here's a way to an traversal using a deque (as a stack) to keep track of the ancestors:
from collections import deque
def delete_iterative(node):
stack = deque()
last = None
# start up by pushing nodes to the stack until reaching leftmost node
while node:
stack.append(node)
node = node.left
# the main loop
while stack:
node = stack.pop()
# should we expand the right subtree?
if node.right && node.right != last: # yes
stack.append(node)
node = node.right
while node: # expand to find leftmost node in right subtree
stack.append(node)
node = node.left
else: # no, we just came from there (or it doesn't exist)
# delete node's contents
node.data.cleanup()
node.data = None # clear pointers (not really necessary)
node.left = None
node.right = None
# let our parent know that it was us it just visited
last = node
An iterative post-order traversal using a stack could look like this:
def is_first_visit(cur, prev):
return prev is None or prev.left is cur or prev.right is cur
def visit_tree(root):
if root:
todo = [root]
previous = None
while len(todo):
node = todo[-1]
if is_first_visit(node, previous):
# add one of our children to the stack
if node.left:
todo.append(node.left)
elif node.right:
todo.append(node.right)
# now set previous to ourself and continue
elif previous is node.left:
# we've done the left subtree, do right subtree if any
if node.right:
todo.append(node.right)
else:
# previous is either node.right (we've visited both sub-trees)
# or ourself (we don't have a right subtree)
do_something(node)
todo.pop()
previous = node
do_something does whatever you want to call "actually deleting this node".
You can do it a bit more simply by setting an attribute on each node to say whether it has had do_something called on it yet, but obviously that doesn't work so well if your nodes have __slots__ or whatever, and you don't want to modify the node type to allow for the flag.
I'm not sure what you're doing with those conditions after the recursive calls, but I think this should be enough:
def delete(root):
if root:
delete(root.left)
delete(root.right)
root = None
As pointed out in comments, Python does not pass parameters by reference. In that case you can make this work in Python like this:
def delete(root):
if root:
delete(root.left)
delete(root.right)
root.left = None
root.right = None
Usage:
delete(root)
root = None
As for an iterative approach, you can try this. It's pseudocode, I don't know python. Basically we do a BF search.
delete(root):
make an empty queue Q
Q.push(root)
while not Q.empty:
c = Q.popFront()
Q.push(c.left, c.right)
c = None
Again, this won't modify the root by default if you use it as a function, but it will delete all other nodes. You could just set the root to None after the function call, or remove the parameter and work on a global root variable.

Python (yield): all paths from leaves to root in a tree

I want to generate all paths from every leaf to root in a tree. I'd like to do that with generators, to save memory (tree can be big). Here's my code:
def paths(self, acc=[]):
if self.is_leaf():
yield [self.node]+acc
for child in self.children:
child.paths([self.node]+acc)
But it doesn't work. Why? Invoked at root, it traverses the tree from top to bottom, collecting nodes in "acc". "acc" should be returned in every leaf...
is_leaf() is true if self.children is empty.
This code only yields leaves that are (immediate) children of the root. The other ones get visited, they yield to the upper function, but the upper function does nothing with them. What you need is to yield them from the lower function to the upper one:
def paths(self, acc=[]):
if self.is_leaf():
yield [self.node]+acc
for child in self.children:
for leaf_path in child.paths([self.node]+acc): # these two
yield leaf_path # lines do that
This should do the trick.
At the moment the for loop doesn't yield anything. It should instead yield all the elements that are generated by the recursive call:
for child in self.children:
for path in child.paths([self.node]+acc):
yield path

Fix depth tree in Python

I want to implement a tree structure which has fixed depth, i.e. when adding children to the leef nodes, the whole tree structure should "move up". This also means that several roots can exist simultaneously. See example beneath:
In this example, the green nodes are added in iteration 1, deleting the top node (grey) and making the two blue nodes at K=0 and Iteration 1 root nodes.
How do I go about implementing this?
Store each node with a reference to its parent. When you add a node to it as a child, walk up the parents (from the node being added to) and delete the third one after you set the parent reference in all of its children to None. Then add the children of the deleted node to your list of trees.
class Node(object):
depth = 4
def __init__(self, parent, contents):
self.parent = parent
self.contents = contents
self.children = []
def create_node(trees, parent, contents):
"""Adds a leaf to a specified node in the set of trees.
Note that it has to have access to the container that holds all of the trees so
that it can delete the appropriate parent node and add its children as independent
trees. Passing it in seems a little ugly. The container of trees could be a class
with this as a method or you could use a global list. Or something completely
different. The important thing is that if you don't delete every reference to the
old root, you'll leak memory.
"""
parent.children.append(Node(parent, contents))
i = 0:
L = Node.depth - 1
while i < L:
parent = parent.parent
if not parent:
break
i += 1
else:
for node in parent.children:
node.parent = None
trees.extend(parent.children)
i = trees.find(parent)
del trees[i]

Categories

Resources