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()
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.
Update
Thanks to the comments of some community members, I realize that there are some similar problems, but they may a bit different, please allow me to explain it further.
I actually hope to use the same method in a real problem, So briefly:
Reuse of edges in differernt path is completely allowed
a unique(or a new) path from A to B is defined as a collection of vertices that have any different vertices.
Let me use a quiz from Python data structure and algorithm analysis by Bradley .N Miller and David L. Ranum to expain my qusetion.
Quesion:
Consider the task of converting the word FOOL to SAGE, also called word ladder problem. In solving
In the word ladder problem, only one letter must be replaced at a time, and the result of each step must be a word, not non-existent.
Input:
FOUL
FOOL
FOIL
FAIL
COOL
FALL
POOL
PALL
POLL
POLE
PALE
PAGE
SALE
POPE
POPE
SAGE
We can easily find the path from FOOL to SAGE, as Bradley showed:
enter image description here
and I used Breadth First Search (BFS) to solve probem:
class Vertex:
def __init__(self, key, value = None):
self.id = key
self.connectedTo = {}
self.color = 'white'
self.dist = sys.maxsize
self.pred = []
self.disc = 0
self.fin = 0
self.value = value,
#self.GraphBulided = False
self.traverseIndex = 0
self.predNum = 0
def addNeighbor(self, nbr, weight=0):
self.connectedTo[nbr] = weight
def __str__(self):
return '{} connectedTo: {}'.format(self.id, \
str([x.id for x in self.connectedTo]))
def setColor(self, color):
self.color = color
def setDistance(self, d):
self.dist = d
#I want store all Pred for next traverse so I use a list to do it
def setPred(self, p, list = False):
if not list:
self.pred = p
else:
self.pred.append(p)
self.predNum += 1
def setDiscovery(self,dtime):
self.disc = dtime
def setFinish(self,ftime):
self.fin = ftime
#def setGraphBulided(self, tag = True):
# self.GraphBulided = tag
def getFinish(self):
return self.fin
def getDiscovery(self):
return self.disc
def getPred(self):
if isinstance(self.pred, list):
if self.traverseIndex < self.predNum:
return self.pred[self.traverseIndex]
else:
return self.pred[-1]
else:
return self.pred
def __hash__(self):
return hash(self.id)
def getPredById(self):
if self.traverseIndex < self.predNum and isinstance(self.pred, list):
pred = self.pred[self.traverseIndex]
self.traverseIndex += 1
print("vertix {}: {} of {} preds".format(self.id, self.traverseIndex, self.predNum))
return [pred, self.traverseIndex]
else:
pred = None
return [pred, None]
def getCurrPredStaus(self):
#if not self.pred:
# return None
return self.predNum - self.traverseIndex
def getDistance(self):
return self.dist
def getColor(self):
return self.color
def getConnections(self):
return self.connectedTo.keys()
def getId(self):
return self.id
def getWeight(self, nbr):
return self.connectedTo[nbr]
def getValue(self):
return self.value
def findPath(self, dest):
pass
class Graph:
def __init__(self):
self.vertList = {}
self.numVertics = 0
self.verticsInSerach = set()
self.GraphBulided = False
def addVertex(self, key, value = None):
self.numVertics = self.numVertics + 1
newVertex = Vertex(key, value=value)
self.vertList[key] = newVertex
return newVertex
def getVertex(self, n):
if n in self.vertList:
return self.vertList[n]
else:
return None
def __contains__(self, n):
return n in self.vertList
def addEdge(self, f, t, cost = 0, fvalue = None, tvalue = None):
if f not in self.vertList:
nv = self.addVertex(f, fvalue)
if t not in self.vertList:
nv = self.addVertex(t, tvalue)
self.vertList[f].addNeighbor(self.vertList[t], cost)
def setGraphBulided(self, tag = True):
self.GraphBulided = tag
def getVertices(self):
return self.vertList.keys()
def setGraphBulided(self, tag = True):
self.GraphBulided = tag
def setSerachedVertixs(self, vertix):
self.verticsInSerach.add(vertix)
def getGraphBulided(self):
return self.GraphBulided
def getSerachedVertixs(self):
return self.verticsInSerach
def __iter__(self):
return iter(self.vertList.values())
def __hash__(self):
hashIds = [x for x in self.getVertices()]
if len(hashIds) > 0 and hashIds[0]:
return hash(', '.join(hashIds))
else:
return None
Here are some additional functions for building graphs
def buildGraph(wordFile, DFSgraph = False):
d = {}
g = Graph()
if DFSgraph:
g = DFSGraph()
wfile = open(wordFile)
for line in wfile:
word = line[:-1]
for i in range(len(word)):
bucket = word[:i] + '_' + word[i+1:]
if bucket in d:
d[bucket].append(word)
else:
d[bucket] = [word]
for bucket in d.keys():
for word1 in d[bucket]:
for word2 in d[bucket]:
if word1 != word2:
g.addEdge(word1, word2)
wfile.close()
return g
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
def bfs(g, start, listpred = False):
start.setDistance(0)
start.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(start)
while (vertQueue.size() > 0):
currentVert = vertQueue.dequeue()
if currentVert.getConnections():
g.setSerachedVertixs(currentVert)
for nbr in currentVert.getConnections():
#print('sreach {}'.format(currentVert.getId()))
if (nbr.getColor() == 'white' or nbr.getColor() == 'gray'):
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance() + 1)
if nbr.predNum > 0 and currentVert.getId() not in [x.getId() for x in nbr.pred]:
nbr.setPred(currentVert, listpred)
elif nbr.predNum == 0:
nbr.setPred(currentVert, listpred)
vertQueue.enqueue(nbr)
currentVert.setColor('black')
Therefore, we can easily find the shortest path we need (If we only store one pred for one vertix).
wordGraph = buildGraph('fourletterwords1.txt', DFSgraph=False)
bfs(wordGraph, wordGraph.getVertex('FOOL'), listpred=True)
def traverse(y):
x=y
while(x.getPred()):
print(x.getPred())
x = x.getPred()
print(x.getId())
traverse(wordGraph.getVertex('SAGE'))
However, I still don't know how to trace all the paths correctly, can you give me some suggestions?
FIND path from src to dst ( Dijkstra algorithm )
ADD path to list of paths
LOOP P over list of paths
LOOP V over vertices in P
IF V == src OR V == dst
CONTINUE to next V
COPY graph to working graph
REMOVE V from working graph
FIND path from src to dst in working graph( Dijkstra algorithm )
IF path found
IF path not in list of paths
ADD path to list of paths
I must implement a sum for each level in a binary tree
I must return a list that contains at position i, the ith sum
example: if I have this tree
24
/ \
14 27
/ \ / \
11 20 12 55
I must return [24, 41, 98]
I tried to implement this solution in python
def sommaperlivelli(p, lista):
if p == None:
return
if p.left != None and p.right != None:
lista.append(p.left.key + p.right.key)
sommaperlivelli(p.left, lista)
sommaperlivelli(p.right, lista)
return lista
I can get only the first sum and I can't add the root. How I can do it?
this is the class that I use
class NodoABR:
def __init__(self, key = None, left = None, right = None, parent = None):
self.key = key
self.left = left
self.right = right
self.parent = parent
this is how I add node to the tree
def inserisciNodo(p, n):
if p == None:
return NodoABR(n)
else:
if p.key == n:
return p
elif p.key < n:
rchild = inserisciNodo(p.right, n)
p.right = rchild
rchild.parent = p
else:
lchild = inserisciNodo(p.left, n)
p.left = lchild
lchild.parent = p
return p
this is a BinarySearchTree
in main function I do this
p = NodoABR(24)
p = inserisciNodo(p, 14)
p = inserisciNodo(p, 27)
p = inserisciNodo(p, 11)
p = inserisciNodo(p, 20)
p = inserisciNodo(p, 12)
p = inserisciNodo(p, 55)
print(sommaperlivelli(p,[]))
Assuming you have similar Tree Node class, you can try this & modify to suite your particular needs.
from collections import deque
class Node: # TreeNode eg.
def __init__(self, key):
self.data = key
self.left = None
self.right = None
def LevelSum(root):
answer = []
if (root == None): return 0 # base case
result = root.data
# track of # nodes at every level when traversal, and sum them up
q = deque()
q.append(root)
while len(q): # > 0):
size = len(q)
# Iterate for all the nodes
tot = 0
while size: # meaning --- size > 0
# Dequeue an node from queue
tmp = q.popleft()
# Add this node's value to current sum.
tot += tmp.data
# Enqueue left and right children of dequeued node
if (tmp.left != None): q.append(tmp.left)
if (tmp.right != None): q.append(tmp.right)
size -= 1
print(tot, result)
answer.append(tot)
return answer
Example:
if __name__ == '__main__':
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.right.right = Node(6)
root.right.right.left = Node(9)
root.right.right.right = Node(11)
"""
# Constructed Binary tree is:
# 1
# / \
# 2 3 # 5
# / \ \
# 4 5 6 # 15
# / \
# 9 11 # 20
"""
print("level sum is", LevelSum(root))
[Edit] The PO author wants to learn another approach. (non-queue)
from collections import Counter
from typing import List
class Node:
def __init__(self, key):
self.data = key
self.left = None
self.right = None
def level_sum(root: Node) -> int:
def dfs(node, level):
if not node: return
total[level] += node.data
dfs(node.left,level + 1)
dfs(node.right,level + 1)
total = Counter()
dfs(root, 1)
print(total) # showing each level & its total --- can be commented out
return total.most_common()[0][1] # the largest total
Using your NodoABR class, here's a simple tree traversal, depth-first, where we note the level at each iteration, and use it to store the node values, arr is an array (or list) of lists, one for each level:
# Create sample tree
root = NodoABR(24, NodoABR(14, NodoABR(11), NodoABR(20)), NodoABR(27, NodoABR(12), NodoABR(55)))
def handle_node(nd, level):
# Add a new level list if needed
if len(arr) == level:
arr.append([])
# Append this node value to its proper level list
arr[level].append(nd.key)
# Recurse, increasing the level
if nd.left is not None:
handle_node(nd.left, level+1)
if nd.right is not None:
handle_node(nd.right, level+1)
# Main program
arr = []
handle_node(root, 0)
print(arr)
And this is the output:
[[24], [14, 27], [11, 20, 12, 55]]
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
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.