I want to figure out how to print all nodes at a specific level. Right now, I can get to that level but I can only print out a part of the nodes. How would I get it to print all the nodes from all branches instead of nodes from one branch? I tried recursively calling get_level_nodes but it keeps outputting an error.
import random
class Node(object):
def __init__(self, value):
self.value = value
self.children = []
self.parent = None
def create_children(self, infects, depth):
# root node
if depth == 0:
return
for i in range(infects):
rand2 = random.random()
if rand2 <= 0.37:
if rand2 <= 0.02:
child = Node('NA')
else:
child = Node('CA')
else:
if rand2 <= 0.5:
child = Node('NS')
else:
child = Node('CS')
child.parent = self
child.grandparent = self.parent
self.children.append(child)
# recursive call to create more child nodes
child.create_children(infects, depth-1)
def tree_level(self):
level = 0
p = self.parent
while p:
level += 1
p = p.parent
return level
def print_tree(self):
spaces = ' ' * self.tree_level() * 2
prefix = spaces + '|__' if self.parent else ''
print(prefix + self.value, self.quarantined)
if self.children:
for child in self.children:
if child.value != None:
child.print_tree()
def get_level_nodes(self, cur_level):
level = 0
c = self.children
while c:
level += 1
c = self.children
if level == cur_level:
return c
if __name__ == "__main__":
rand1 = random.random()
if rand1 <= .35:
a = Node('CA')
else:
a = Node('CA')
a.create_children(2, 5) # create_children(R0, depth)
for child in a.get_level_nodes(4):
print(child.value)
a.print_tree()
Your get_level_nodes function has some issues:
c never changes value: it always represents self.children, so you are not actually moving down in the tree. You should somewhere iterate over those children and extend your collection of nodes with the children of these children.
You start out with self.children, but that list of nodes already represents the second level in the tree. You should foresee that the function can return the top-level of the tree, i.e. a list with just the root node in it.
I'll assume that you use the definition of "level" as specified in Wikipedia, although other definitions exist:
Level
1 + the number of edges between a node and the root, i.e. (depth + 1)
Solution:
def get_level_nodes(self, cur_level):
nodes = [self]
for i in range(cur_level-1): # assuming that cur_level is at least 1
children = []
for node in nodes:
children.extend(node.children)
nodes = children
return nodes
Related
I have below method where self contains a data structure as below
self.place = "India"
self.children = ["Tamil Nadu", "Karnataka"]
self.parent
Method
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
So via recursion, I am iterating on all possible child nodes to find the node I am looking for via return node.get_node(value) but I observed that, iteration happening via "Tamil Nadu" but not via "Karnataka".
I understood that, it took the first element of the list and then continued from there, but not coming back to 2nd element of the list.
is this expected behavior from recursion or am I doing something wrong ?
Full code( In case needed for testing)
class TreeNode:
def __init__(self, place):
self.place = place
self.children = []
self.parent = None
def add_child(self, child):
child.parent = self
self.children.append(child)
def print_tree(self):
prefix = ""
if self.parent is None:
print(self.place)
else:
prefix = prefix + (" " * self.get_level() * 3)
prefix = prefix + "|__"
print(prefix + self.place)
for child in self.children:
child.print_tree()
def get_level(self):
level = 0
p = self.parent
while p:
level = level + 1
p = p.parent
return level
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
def tree_map(self, nodes):
for node in nodes:
self.add_child(TreeNode(node))
def build_places():
root = TreeNode("Global")
india = TreeNode("India")
usa = TreeNode("USA")
root.add_child(india)
root.add_child(usa)
india_nodes = ["Gujarat" ,"Karnataka"]
gujarath_nodes = [ "Ahmedabad", "Baroda"]
karnataka_nodes = ["Bangalore", "Mysore"]
usa_nodes = ["New Jersey", "California"]
newjersey_nodes = ["Princeton", "Trenton"]
california_nodes = ["San Franciso", "Mountain View", "Palo Alto"]
for node in india_nodes:
india.add_child(TreeNode(node))
for node in usa_nodes:
usa.add_child(TreeNode(node))
gujarath_node = root.get_node("Gujarat")
print(gujarath_node.place)
for node in gujarath_nodes:
gujarath_node.add_child(TreeNode(node))
karnataka_node = root.get_node("Karnataka")
print(karnataka_node.place)
return root
if __name__ == "__main__":
root = build_places()
root.print_tree()
The problem is that in your loop you are always exiting the loop in its first iteration (when the node has at least some children). You should only exit on success, not when the recursive call comes back without success.
So change the loop to this:
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
result = node.get_node(value)
if result:
return result
Secondly, there is a strange base case you have at the start of this function. I would replace this:
if value is None:
return self
With:
if value is None:
return None
...since you didn't look for the value in that case: so then (in my opinion) it is not right to return a node instance (which might have any value -- you didn't verify it). It seems more consistent to return None or to remove this whole if block and not treat None in a special way.
I am a starter & want to integrate dfs code with Fibonacci series generating code. The Fibonacci code too runs as dfs, with calls made from left to right.
The integration is incomplete still.
I have two issues :
(i) Unable to update 'path' correctly in fib(), as the output is not correctly depicting that.
(ii) Stated in fib() function below, as comment.
P.S.
Have one more issue that is concerned with program's working:
(iii) On modifying line #16 to: stack = root = stack[1:]; get the same output as before.
import sys
count = 0
root_counter = 0
#path=1
inf = -1
node_counter = 0
root =0
def get_depth_first_nodes(root):
nodes = []
stack = [root]
while stack:
cur_node = stack[0]
stack = stack[1:]
nodes.append(cur_node)
for child in cur_node.get_rev_children():
stack.insert(0, child)
return nodes
def node_counter_inc():
global node_counter
node_counter = node_counter + 1
class Node(object):
def __init__(self, id_,path):
self.id = node_counter_inc()
self.children = []
self.val = inf #On instantiation, val = -1, filled bottom up;
#except for leaf nodes
self.path = path
def __repr__(self):
return "Node: [%s]" % self.id
def add_child(self, node):
self.children.append(node)
def get_children(self):
return self.children
def get_rev_children(self):
children = self.children[:]
children.reverse()
return children
def fib(n, level, val, path):
global count, root_counter, root
print('count :', count, 'n:', n, 'dfs-path:', path)
count += 1
if n == 0 or n == 1:
path = path+1
root.add_child(Node(n, path))
return n
if root_counter == 0:
root = Node(n, path)
root_counter = 1
else:
#cur_node.add_child(Node(n, path)) -- discarded for next(new) line
root.add_child(Node(n, path))
tmp = fib(n-1, level + 1,inf, path) + fib(n-2, level + 1,inf,path+1)
#Issue 2: Need update node's val field with tmp.
#So, need suitable functions in Node() class for that.
print('tmp:', tmp, 'level', level)
return tmp
def test_depth_first_nodes():
fib(n,0,-1,1)
node_list = get_depth_first_nodes(root)
for node in node_list:
print(str(node))
if __name__ == "__main__":
n = int(input("Enter value of 'n': "))
test_depth_first_nodes()
Want to add that took idea for code from here.
Answer to the first question:
Path in this particular question is an int. It is a numbering of path from the root to a leaf in a greedy dfs manner.
This can be achieved by letting path be a global variable rather than an input to fib function. We increment the path count whenever we reach a leaf.
I have also modified the fib function to returns a node rather than a number.
import sys
count = 0
root_counter = 0
path=1
inf = -1
node_counter = 0
root = None
def node_counter_inc():
global node_counter
node_counter = node_counter + 1
print("node_counter:", node_counter)
return node_counter
class Node(object):
def __init__(self, id__,path):
print("calling node_counter_inc() for node:", n )
try:
self.id = int(node_counter_inc())
except TypeError:
self.id = 0 # or whatever you want to do
#self.id = int(node_counter_inc())
self.val = inf #On instantiation, val = -1, filled bottom up;
#except for leaf nodes
self.path = path
self.left = None
self.right = None
def __repr__(self):
return "Node" + str(self.id) + ":"+ str(self.val)
def fib(n, level, val):
# make fib returns a node rather than a value
global count, root_counter, root, path
print('count :', count, 'n:', n, 'dfs-path:', path)
count += 1
if n == 0 or n == 1:
path = path+1
new_Node = Node(n, path)
new_Node.val = n
return new_Node
#root.add_child(new_Node)
# return new_node
#if root_counter == 0:
# root = Node(n, path)
# root_counter = 1
#else:
#cur_node.add_child(Node(n, path)) -- discarded for next(new) line
# root.add_child(Node(n, path))
#tmp = fib(n-1, level + 1,inf) + fib(n-2, level + 1,inf)
#Issue 2: Need update node's val field with tmp.
#So, need suitable functions in Node() class for that.
#print('tmp:', tmp, 'level', level)
#return tmp
ans = Node(n, path)
ans.left = fib(n-1, level + 1, inf)
ans.right = fib(n-2, level + 1, inf)
ans.val = ans.left.val + ans.right.val
print("the node is", ans.id, "with left child", ans.left.id, "and right child", ans.right.id)
print("the corresponding values are", ans.val, ans.left.val, ans.right.val)
return ans
def test_depth_first_nodes():
ans = fib(n,0,-1)
print("The answer is", ans.val)
#node_list = get_depth_first_nodes(root)
#for node in node_list:
# print(str(node))
if __name__ == "__main__":
n = int(input("Enter value of 'n': "))
test_depth_first_nodes()
I am somehow new to python. I needed to use tree to store some data (file paths), The problem is when I generate the tree it seems that all objects after the root reference the same object, although step by step debugging showed the opposite. Here is my (minimized) code:
first the node class:
class PathElement:
Element = ""
IsStatic = True
Children = []
ChildrenCount = 0
def __init__(self, Element, IsStatic=True):
self.Element = Element
self.IsStatic = IsStatic
if not IsStatic:
self.Element = []
def AddChild(self, Child):
print(self, " ", Child)
self.Children.append(Child)
self.ChildrenCount = len(self.Children)
return Child
The Children is list of PathElement nodes. The code that build the tree:
def UnFoldAndCheck(self):
Path = PathElement("root")
Handler = Path
Index = 0
Count = len(self.Path)
while Index < Count:
element = self.Path[Index]
if something:
Child = None
Child = PathElement(element)
Handler.AddChild(Child)
Handler = None #Those added to debug the problem
Handler = Child
elif other_thing:
if condition:
if some_large_condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 5):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 4
elif another_condition:
ChildOp = None
ChildOp = PathElement(element, False)
Handler.AddChild(ChildOp)
Handler = None
Handler = ChildOp
elif some_else_condition:
if condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 3):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 2
elif different_condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 3):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 1
Index += 1
return Path
My problem is that after the tree is built when I use it it will have always same structure:
root -> object with 3 exact nodes -> same object -> same object to infinity
while the expected is:
root -> object -> first children -> second children -> third children -> etc
I'm sure the problem is related to how python handle object references but I can't see where the problem exactly. Any help?
Update:
I reproduced the problem with smaller code (same class PathElement):
from PathElement import PathElement
Path = PathElement("root")
Handler = Path
for i in range(1,6):
Child = PathElement("child"+str(i))
Handler.AddChild(Child)
Handler = Child
Tree = Path
while True:
print(Tree.Element)
if len(Tree.Children) > 0:
Tree = Tree.Children[0]
else:
break
This code will make infinite loop
I guess you come from Java or a similar language. It's important to stick with Python's conventions (Jakob Sachs gave you va link to the Style Guide for Python Code) because that makes your mistakes are easier to identify.
Now, what's wrong here? When you wrote:
class PathElement():
Children = []
Element = ""
IsStatic = True
ChildrenCount = 0
You don't give the initial value of instance fields. You create an initialize class (static) fields. Hence, Children is a static field of the class PathElement. Here's a illustration of that:
class A():
i = []
a = A()
b = A()
a.i.append(1)
b.i.append(2)
assert a.i == b.i == [1,2]
What happens when you try to make read the leftmost part of the tree (child 0, child 0 of child 0, ...)?
while True:
print(Tree.Element)
if len(Tree.Children) > 0:
Tree = Tree.Children[0]
else:
break
Just replace Tree.Children by what it is really: PathElement.Children, that is the static field Children of the class PathElement:
while True:
print(Tree.Element)
if len(PathElement.Children) > 0:
Tree = PathElement.Children[0] # Tree has always the same value.
else:
break
Now, a example of what you can write:
class PathElement:
def __init__(self, element):
self.__element = element
self.__children = []
def add_child(self, child):
self.__children.append(child)
def children(self):
return list(self.__children)
def element(self):
return self.__element
path = ["a", "b", "c", "d", "e", "f"]
root = PathElement("root")
handler = root
while path:
child = PathElement(path.pop(0)) # you can put some conditions here, take more elements of path, ...
handler.add_child(child)
handler = child
def dfs(node):
for c in node.children():
yield c.element()
yield from dfs(c)
print (list(dfs(root)))
# a b c d e f
I'm trying to build a binary search tree class and get a name error for:
children_num for this children_num == node.children_count()
This is the code that uses children_num:
def delete(self, value):
'''delete node containing value'''
node, parent = self.lookup(value)
if node is not None:
children_num == node.children_count()
if children_num == 0:
# first case- if there are no children you can just remove node
if parent:
if parent.leftChild is node:
parent.leftChild = None
else:
parent.rightChild = None
del node
else:
self.value = None
elif children_num == 1:
# case 2- if node has 1 child->replace node with its child
if node.leftChild:
n = node.leftChild
and this is the children_count function:
def children_count(self):
# returns the number of children: 0 , 1 , 2
total=0
if self.leftChild:
total += 1
if self.rightChild:
total += 1
return total
I tried entering children_num in the __init__ (self.children_num= None), making it a global var (global children_num) and define it as children_num==0, none of these worked.
I have a non-binary tree and each node of that tree has a value. I would like to get the maximum sum available.
For example:
10
9 8 7
1 2 5 5 15
The return would be 10+7+15=32
I know how to do this if the tree was binary, but what if the tree has n branches?
This code is the binary one taken from the first answer of this question: Find the maximum sum of a tree in python
Assuming each node has a value attribute and a children attribute which is either a list of child nodes, an empty list, or None:
def tree_sum(node):
if node.children:
child_sums = []
for child in node.children:
child_sums.append(tree_sum(child) + node.value)
return max(child_sums)
else:
return node.value
print tree_sum(root)
Here's one approach:
class Node:
def __init__(self, value):
self.value = value
self.children = []
def max_sum(root):
if len(root.children) == 0:
return root.value
sums = []
for child in root.children:
sums.append(root.value + max_sum(child))
return max(sums)
n_9 = Node(9)
n_9.children.extend([Node(1), Node(2), Node(5)])
n_8 = Node(8)
n_8.children.extend([Node(5)])
n_7 = Node(7)
n_7.children.extend([Node(15)])
n_10 = Node(10)
n_10.children = [n_9, n_8, n_7]
print max_sum(n_10)
# Output: 32