Get child node if exists otherwise create in Python - python

I have a basic class containing two attributes 'Name' and 'Children.
class Node():
def __init__(self, name):
self.name = name
self.children = []
I have a core root node as follows:
# Create Master Root Node
root = Node('root')
How would I go about creating a function that I could pass a path to a specific node in the tree, and the function return that node.
However if that node doesn't exist it would create/append to the tree, and still return the node.
path = ['Leslie','Marie','Tori'] # simple example
def get_node_by_path(path=[])...
If a path failed before reaching the end of the path, it would automatically create the missing nodes in order to make the entire path complete.
path = ['Leslie','Marie','Tori','Kevin'] # more complex requires two nodes to be created
def get_node_by_path(path=[])...

I'd do something like this. It's a non recursive solution.
def get_node_by_path(path):
cur_node = root
for elem_name in path:
found = False
for child in cur_node.children:
if child.name == elem_name:
cur_node = child
found = True
break
if not found:
new_node = Node(elem_name)
cur_node.children.append(new_node)
cur_node = new_node
return cur_node

class Node:
def __init__(self, name):
self.name = name
self.children = []
def path_to(self, path):
if not path:
return self
head, *tail = path
for child in self.children:
if child.name == head:
return child.path_to(tail)
newborn = Node(head)
self.children.append(newborn)
return newborn.path_to(tail)
Here's a solution that recursively considers whether the first name in the list is a child of the current node.
For Python 2, head, *tail = path can be replaced with
head = path[0]
tail = path[1:]

start with the last dag object and go to the root
class Node():
def __init__(self, name = "", childern = []):
self.name = name
self.children = childern
print ("Node Name: {0} Childern Nodes {1}".format(self.name, self.children))
def get_node_by_path(path=[]):
for c,n in enumerate(reversed(path)):
if not c:
Node(name = n)
else:
Node(name = n, childern = path[-c:])
path = ['root', 'Leslie','Marie','Tori','Kevin']
get_node_by_path(path)

Related

Python: Recursion not iterating all elements of the list

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.

BFS / level order traversal in Non-Binary Tree using queue

I have to make two classes: NonBinaryTree and SingleNode class containig some methods working on nodes as well as entire tree (in class NonBinaryTree). I have encountered problems with implementing BFS (level order) traversal through Non Binary Tree using queue (first in, first out type). As there are many resources for Binary Tree, where each node have up to two children, I have not found anything that could help me solve problem with Non Binary Tree.
So far, I made this code:
import queue
from typing import List, Callable
class SingleNode:
def __init__(self, name : str):
self.name : str = name
self.children : List['SingleNode'] = []
def add(self, *nodes : List['SingleNode']):
for node in nodes:
self.children.append(node)
def is_leaf(self):
if len(self.children) == 0:
return True
return False
def level_order_traversal(self, visit: Callable[['SingleNode'], None]) -> List[List[int]]:
fifo = queue.Queue()
levels = []
fifo.put([root])
while fifo and root:
currNode, nextLevel = [], []
while not fifo.empty():
node = fifo.get()
currNode.append(node)
for child in node.children:
nextLevel.append(child)
fifo.put(nextLevel)
levels.append(currNode)
return levels
def search(self, name : str):
if self.name == name:
print(self.__repr__())
for child in self.children:
child.search(name)
return None
def __str__(self):
return f"{self.name}"
def __repr__(self):
return f"TreeNode({self.name}) : {self.children}"
class NonBinaryTree:
root_node: SingleNode
My tree:
enter image description here
I need to go on nodes in this order: 1, 2, 3, 4, 5, 6, 7, 8, and so on...
Why don't you follow similar approach as BFS traversal in binary tree, it's just in this case it's non binary but the logic is always gonna be same,
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
levels = []
queue = [root]
while queue and root:
currNode,nextLevel = [],[]
for node in queue:
currNode.append(node.val)
for child in node.children:
nextLevel.append(child)
queue = nextLevel
levels.append(currNode)
return levels

NameError: name "AnyName" is not defined

I am trying to do an N-Ary tree based on Linked-Lists and Nodes. But whenever I try to add a new value to the tree I keep getting:
NameError: name 'self' is not defined
I work with modules so I have to import the classes from other files.
I get this error in def addTree(self, value, parent = self.root): on Tree Code
Tree Code
from Resources.LinkedList import *
class Tree:
def __init__(self):
self.root = LinkedList()
def addTree(self, value, parent = self.root):
parent.addLinkedList(value)
Node Code
from Resources.LinkedList import *
class Node:
def __init__(self,name):
self.name = name
self.children = LinkedList()
self.next = None
Linked-List Code
from Resources.Node import *
from Resources.Compare import *
class LinkedList:
def __init__(self):
self.first = None
def addLinkedList(self,value):
if (not self.first):
self.first = Node(value)
else:
compare = Compare()
if(compare.compare(self.first,value)>0):
stack = self.first
self.first = Node(value)
self.first.next = stack
return True
else:
previous = self.first
current = self.first.next
while(current):
if (compare.compare(current,value)<0):
previous = current
current = current.next
return True
elif (compare.compare(current,value)>0):
stack = current
previous.next = Node(value)
previous.next.next = stack
return True
else:
previous.next = Node(value)
previous.next.next = current.next
return True
previous.next = Node(value)
return True
Also thanks for your help, I'm kinda new to Python and I don't know what I am doing wrong.
The problem is that "self" is only defined within the method and cannot be used in the arguments, the trick in these cases is to use None as an argument and make the verification:
def addTree(self, value, parent = None):
if parent is None:
parent = self.root
parent.addLinkedList(value)

Dijkstra's Algorithm - wrong order of nodes in shortest path

I've been working on a school assignment, where I need to implement Dijkstra's algorithm. That wouldn't be too hard by itself but unfortunately, the automatic checking script disagrees with all of my implementations (I actually made like 8 different versions). All the initial data checking works correctly, only when the script generates random data, it differs. My path and script's path has the same distance, but different vertexes on the path. For example:
Teachers path: City2, City15, City16, City6,
Students path: City2, City15, City18, City0, City6,
I even contacted the teacher who just responded with "You have to use priority queue :-)" despite me using one (in fact, several implementations of one, from my own to heapq). Am I doing something wrong or is it the teacher script that's incorrect? I hope the code is self-commenting enough to be understandable. Thank you for any advice you can give me.
The algorithm is called on source vertex and computes shortest distance and path to every other connected node. If the vertex has same minDistance (ie. priority) as some that's already there, it should go in front of it, not after it.
class Node:
"""Basic node of the priority queue"""
def __init__(self, data, priority):
self.data = data
self.nextNode = None
self.priority = priority
self.id = data.id
class PriorityQueue:
"""Basic priority queue with add, remove and update methods"""
def __init__(self):
self.head = None
self.count = 0
def add(self, data, priority):
"""Adds data with priority in the proper place"""
node = Node(data, priority)
if not self.head:
self.head = node
elif node.priority <= self.head.priority:
node.nextNode = self.head
self.head = node
else:
checker = self.head
while True:
if not checker.nextNode or node.priority >= checker.nextNode.priority:
break
checker = checker.nextNode
node.nextNode = checker.nextNode
checker.nextNode = node
return 0
def remove(self, data):
"""Removes specified node and reconnects the remaining nodes, does nothing if node not found"""
checker = self.head
if not self.head:
return 0
if checker.id == data.id:
self.head = checker.nextNode
while True:
checker = checker.nextNode
if not checker or not checker.nextNode:
return 0
if checker.nextNode.id == data.id:
checker.nextNode = checker.nextNode.nextNode
break
return 0
def update(self, data):
"""Updates priority of existing node via removing and re-adding it"""
self.remove(data)
self.add(data, data.minDistance)
return 0
def getMin(self):
"""Returns the minimum priority data"""
min = self.head
return min.data
class Edge:
"""Edge of the graph, contains source, target and weight of line"""
def __init__(self, source, target, weight):
self.source = source
self.target = target
self.weight = weight
class Vertex:
"""Vertex of the graph, everything except id and name is filled later"""
def __init__(self, id, name):
self.id = id
self.name = name
self.minDistance = float('inf')
self.previousVertex = None
self.edges = []
self.visited = False
class Dijkstra:
"""Dijkstra's algorithm implementation"""
def __init__(self):
self.vertexes = []
self.nodes = {}
self.unvisited = PriorityQueue()
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
def getVertexes(self):
"""Returns vertexes in graph, should be called after creating it just to check"""
return self.vertexes
def computePath(self, sourceId):
"""Fills in minDistance and previousVertex of all nodes from source"""
mainNode = self.nodes[sourceId]
mainNode.minDistance = 0
self.unvisited.add(mainNode, 0)
while self.unvisited.head:
mainNode = self.unvisited.getMin()
mainNode.visited=True
for edge in mainNode.edges:
tempDistance = mainNode.minDistance + edge.weight
targetNode = self.nodes[edge.target]
self.unvisited.remove(mainNode)
if tempDistance < targetNode.minDistance:
targetNode.minDistance = tempDistance
targetNode.previousVertex = mainNode
self.unvisited.update(targetNode)
return 0
def getShortestPathTo(self, targetId):
"""Returns list of shortest parth to targetId from source. Call only after doing ComputePath"""
path = []
mainNode = self.nodes[targetId]
while True:
path.append(mainNode)
mainNode = mainNode.previousVertex
if not mainNode:
break
return list(reversed(path))
def resetDijkstra(self):
"""Resets ComputePath but leaves graph untouched"""
for vertex in self.vertexes:
vertex.minDistance = float('inf')
vertex.previousVertex = None
return 0
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
I belive this was wrong => edgesToVertexes.remove(edge)
I had similar home work and used some of your code and this one line was incorrect I think. It removed one path from vortex in every loop.

Python not returning the same object?

Here is my code:
class Node:
nodes = {}
def __init__(self, name):
self.name = name
self.parent = []
self.children = []
Node.nodes[self.name] = self
def addParent(self, parent):
print "adding parent %s for %s " % (parent, self.name)
self.parent.append(parent)
print self.parent
def addChild(self, child):
self.children.append(child)
def removeParent(self, parent):
try:
self.parent.remove(parent)
except:
pass
def removeChild(self, child):
try:
self.children.remove(child)
except:
pass
def lookup(obj):
print "calling look up"
Node.nodes.get(obj)
def create_node(obj):
return lookup(obj) or Node(obj)
# Tree has Nodes
class Tree:
trees = {}
def __init__(self, name, root):
self.name = name
self.root = root
self.size = 1
self.nodes = set() # tree has unique nodes
self.nodes.add(root)
Tree.trees[self.name] = self
def addNode(self, node):
self.nodes.add(node)
self.size += 1
def removeNode(self, node):
try:
self.nodes.remove(node)
except:
return
self.size -= 1
def setRoot(self, root):
self.root = root
def print_tree(self):
for i in self.nodes:
if i == self.root.name:
print "root: %s" % i
else:
print i
def main():
roota = create_node("a")
ta = Tree("a", roota)
childaa = create_node("a_a")
roota.addChild(childaa)
childaa.addParent(roota)
ta.addNode(childaa)
childab = create_node("a_b")
roota.addChild(childab)
childab.addParent(roota)
ta.addNode(childab)
# make one of the child of a root
rootb = create_node("a_a") # should give me a node that already exists from the above tree
tb = Tree("a_a", rootb)
childbb = create_node("a_b") # this node should have two parents now, a and a_a
rootb.addChild(childbb)
childbb.addParent(rootb)
tb.addNode(childbb)
for node in Node.nodes.itervalues():
print "Name: %s" % node.name
if node.parent:
print "Parent: %s" % [parent.name for parent in node.parent]
else:
print "Parent: %s" % node.parent
print "Children: ", [node.name for node in node.children]
print ""
if __name__ == '__main__':
main()
And the output of the script:
Name: a
Parent: []
Children: ['a_a', 'a_b']
Name: a_a
Parent: []
Children: ['a_b']
Name: a_b
Parent: ['a_a']
Children: []
a_a should have parent a. line 80 is adding a as a parent for a_a
a_b should have parent a_a and a. line 85 is adding a as a parent for a_b
can someone explain to me why that's not the case in this code?
And desired the output of the script:
Name: a
Parent: []
Children: ['a_a', 'a_b']
Name: a_a
Parent: ['a']
Children: ['a_b']
Name: a_b
Parent: ['a', 'a_a']
Children: []
A tree is a directed cyclic-free graph. Each node of a tree is itself a tree, hence you don't need two classes for tree and node (except you want to give some metainfo to your tree).
It is sufficient to keep track either of children or of the parent, but for convenience sake (e.g. transversing the tree in both directions) you can save both. But if you do, you have to take care that isParent(a,b) be tautological to isChild(b,a). In your code, if you add a node and don't manually set its parent, your tree goes haywire.
Said this, "# this node should have two parents now, a and a_a" doesn't make much sense if we are talking about trees.
A basic tree structure looks like this (not validating for cycles):
class Tree:
def __init__ (self, payload):
self.payload = payload
self.children = set ()
self.parent = None
def __iadd__ (self, child):
if child.parent: raise Exception ('Node already attached')
child.parent = self #update parent
self.children.add (child) #update children's list
return self
def detach (self):
if not self.parent: return
self.parent.children.remove (self) #update parent's children's list
self.parent = None #update self
def pprint (self, level = 0):
print (' ' * level + self.payload)
for child in self.children:
child.pprint (level + 2)
And an example is this:
root = Tree ('root')
a = Tree ('A')
b = Tree ('B')
c = Tree ('C')
root += a
root += b
b += c
root.pprint ()
c.detach ()
a += c
root.pprint ()
I hope you can take some ideas from this snippet about how to build a tree.

Categories

Resources