Walk through decision tree and capture each node - python

I'm trying to walk through a Decision Tree one node at a time.
Each node can have 2-3 paths
On the nodes with 3 paths, one of the paths is always an end point, and one is sometimes and end point
We can't move backwards, but can start over
Our available functions are
getCurrentNode() #returns string of current node's path from start (ex. 'A-B-A-A-B')
getCurrentNodePaths() #returns number of possible paths from this node
startOver() #puts us back at node 0
takePath(int pathNumber) #traverse the decision tree down a desired path
I've written this pseudo code that should walk through each node recursively, but only for the 'left' most path
# Start
def walk(pathNumber):
takePath(pathNumber)
next_nodes_paths = getCurrentNodePaths()
if next_nodes_paths.length > 0:
walk(0)
startOver()
walk(0)
How can I get this to keep track of where it's been, start over, and take a new path

This creates a model of the decision tree. You can navigate to a certain Node with the select_path method. path is a string like '03231002'. You can walk over the whole tree and apply a function at each point, by using the apply_function method. There is an example for walking the whole tree.
def select_path(path):
startOver()
for pathNumber in path:
takePath(int(pathNumber))
class Node:
def __init__(self,path):
self.path = path
self.select()
self.num_children = getCurrentNodePaths().length
self.children = [Node(path+str(i)) for i in range(self.num_children)]
def select(self):
select_path(self.path)
def apply_function(self, func, recursive=False):
self.select()
func()
if recursive:
for child in self.children:
apply_function(self, func, recursive=True)
root = Node('')
#walk whole tree and apply function function:
#def function:
# pass
#root.apply_function(function, recursive=True)

Since we cannot move backwards, your approach of (recursive) depth-first search is probably difficult to handle: You can never know if a node is an end node without actually moving there, and once you have arrived, you can only make a new walk to the previous node all the way from the start.
I suggest using a breadth-first search instead (partly adopted from this example):
def walk(currentNode):
queue = []
visited = []
queue.append(currentNode)
visited.append(currentNode)
while queue:
s = queue.pop(0)
# go to node from start by following the path
startOver()
for p in s:
takePath(int(p))
for i in range(getCurrentNodePaths()):
nextNode = getCurrentNode() + str(i)
queue.append(nextNode)
visited.append(nextNode)
# Use this if you want a list of paths to end points only
# if getCurrentNodePaths() > 0:
# visited.remove(s)
print(visited)
startOver()
walk(getCurrentNode())
This will give you a list of paths to all nodes in your tree in visited.
A few notes:
A node in the queue and visited lists is assumed to be represented by its path from the start node as a string (e.g. 0, 101, 012012).
Thus, a node can be reached by just following its sequence of numbers.
Moreover, the successor nodes can be constructed by
appending the numbers within range(getCurrentNodePaths()).

Related

How to go back to pre-visited node in a binary tree (preorder traversal, solving No.1028 problem in LeetCode )

I'm struggling to solving No.1028 problem in LeetCode by myself. As a step of my solution, I wanna construct a binary tree below by adding nodes one by one in an order of 'preorder traversal'.
When I'm done with leftmost nodes with moving the cursor., now I'm supposed to add 'TreeNode(5)' as a right child node of 'TreeNode(2)'.
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
root = TreeNode(1)
# cursor = root
# Add TreeNode(N) as a childNode
# cursor = cursor.left
# ...repeat
# Now cursor reaches TreeNode(4)
However, I have no idea of bactracking to the pre-visited nodes.
I tried to do so by stacking pre-visited nodes in my stack and popping few times.
tmp = [TreeNode(1), TreeNode(2), TreeNode(3), TreeNode(4)]
tmp.pop()
tmp.pop()
cursor = tmp.pop()
But only to fail since cursor does contain TreeNode(2) but the tree-relationship between nodes are deleted (I know actually they are not deleted but just cursor becomes another class object).
Could anyone let me know how to visit parent node?
I tried to do so by stacking pre-visited nodes in my stack and popping few times.
That really is the way to do it.
tmp = [TreeNode(1), TreeNode(2), TreeNode(3), TreeNode(4)]
You should only create nodes as you process them from the input, and append them to the stack (using stack.append(node)). Don't recreate nodes, as those will be different objects.
Before appending the node to the stack, you should first verify what the level is you get from the input. That should become the size of the stack -- so if the stack is larger than the level, then pop elements from it until the stack has the right size. Only then append the new node to the stack.
Finally, you need to establish the relationship between new node and its parent. Its parent is the preceding node on the stack. Remains to find out whether the new node should become a left child or a right child. If the parent happens to already have a left child, then make the new node the right child of the parent, otherwise it should become the left child.
With these ingredients you should be able to code it.
Here is a spoiler solution:
import re # Use regular expression to easily tokenise the input
class Solution:
def recoverFromPreorder(self, s):
stack = []
for dashes, valstr in re.findall("(-*)(\d+)", s):
level = len(dashes)
node = TreeNode(int(valstr))
del stack[level:] # Potentially truncate the stack
if level: # This node has a parent: establish link
if not stack[-1].left:
stack[-1].left = node
else:
stack[-1].right = node
stack.append(node)
return stack[0]
# Test run
root = Solution().recoverFromPreorder("1-2--3---4-5--6---7")

Checking for a loop in an un-directed tree graph python

I am trying to develop a python program to check if there is a loop in the graph when a new line is added.
I am storing different lines in a list ordered by shortest length first, and the lines are a class:
class Line():
def __init__(self,node1,node2,length):
self.node1 = node1
self.node2 = node2
self.length = int(length)
self.drawn = False
And the nodes are stored in a list:
nodes = ["A","B","C","D","E","F"]
When my program has run it stores the route as a list:
route = [class(Line),class(Line)...]
What i want it to do is to check that when a new line is added that it does not form a cycle. I plan to use a method inside of a bigger class:
something like:
def check_loop(new_line,graphs):
add new line to graph
if there is a loop in graphs:
return False
else:
return True
(sorry this is one of my first posts so the format is rubbish)
To identify if a cycle is created in a tree is pretty simple, all you need to do is pick a node and then breadth first search the entire tree from that node. If any node has been visited before by the time you reach it, then you know that there is a cycle because there was an alternative path that reached that node beside the one that you have taken.

Large graph as input to find all paths

I'm using the following python code in order to find all paths between each two nodes. It's not any problem for small graphs.
def bfs(graph, start, end):
# maintain a queue of paths
queue = []
# push the first path into the queue
queue.append([start])
while queue:
# get the first path from the queue
path = queue.pop(0)
# get the last node from the path
node = path[-1]
# path found
if node == end:
return path
# enumerate all adjacent nodes, construct a new path and push it into the queue
for adjacent in graph.get(node, []):
new_path = list(path)
new_path.append(adjacent)
queue.append(new_path)
for node3 in graph:
for node4 in graph:
few = bfs(graph, node3, node4)
if not few == None:
print ("{0} -> {1}".format(node3,node4))
print (few)
print ("\n")
But, I want to find all paths between each two nodes, for a large graph with about 4K nodes and 20K edges. The program intrupts and doesn't return any output.
May you please help me how to set the input graph and also, how to set the output to add in a separated file?
Your answer is it may couldn't be done, except the case that your graph is a special graph, number of paths between two nodes in such graph may be enormous.consider following case:
For a complete graph of 200 vertices and 20K edges there is 198!/2 different paths between any two vertices.
If your graph contains a cycle then there is infinite paths in it.
Your graph could be have such number of paths between two vertices that even a super computer couldn't compute the number in a decade.

When to use Depth-first search

So I want to sort a range of numbers so that the sum of every sequential number is a squared number (4,9,16,25,...) I was told I should use Depth first search but am unable to implenment it correctly, especialy the backtracking part. Is this the correct way to do this, what am I not seeing, doing wrong? Is DFS even the correct way to do this?
class Node: #Define node Object
def __init__(self, value):
self.value = value #Node "Name"
self.adjacentNodes=list() #Node Neighbours
#Set limit
limit=16
squares=list() #list for squared numbers in rang smaller than 2Xlimit
tree=list() #Tree containing nodes
path=list() #current path taken
def calculate(limit): #Population control legal squares
for i in range(1,(limit+1)):
i=i*i
if i<(limit+limit):
squares.append(i)
calculate(limit) #Populate squares list
for a in range(1,(limit+1)): #creating the nodes
newnode=Node(a)
for s in squares:
legalsum=s-a
if legalsum>0 and legalsum<limit and legalsum!=a: #zero and lower can't play,keeping non-exiting nodes out of node.adjacentNodes, don't connect to selve.
newnode.adjacentNodes.append(legalsum) #Add adjacentnode
tree.append(newnode) #add newborn to tree
for b in range(0,limit):
print tree[b].value,tree[b].adjacentNodes #Quick checkup
#Now the tree is build and can be used to search for paths.
'''
Take a node and check adjnodes
is it already visited? check path
yes-> are all nodes visited (loop over tree(currentnode).adjnodes) Maybe use len()
yes->pop last node from path and take other node -> fear for infinte loops, do I need a current node discard pile?
no -> visit next available adje node next in line from loop got from len(tree.adjn)
no-> take this as node and check adj nodes.
'''
def dfs (node):
path.append(node)
#load/check adjacent nodes
tree[node].adjacentNodes
for adjnode in tree[node-1].adjacentNodes:
if adjnode in path:
#skip to next adj node
continue
else:
print path #Quick checkup
dfs(adjnode)
dfs(8) # start with 8 since 1-16 should be {8,1,15,10,6,3,13,12,4,5,11,14,2,7,9,16}
I have no idea what you are talking about with the squares but here are some problems
def dfs (node):
#always assume the path starts with this node
path=[node]
if node == GoalState: return path #we win!!
#load/check adjacent nodes
#this line did nothing `tree[node].adjacentNodes`
for adjnode in tree[node-1].adjacentNodes:
# if adjnode in path: ****this will never be true, lets eliminate it
# #skip to next adj node
# continue
# else:
subpath = dfs(adjnode)
if subpath and subpath.endswith(GoalState):
return path + subpath #return solution
#if we get here there is no solution from this node to the goal state

Understanding This Depth First Search Algorithm

I have been given a DFS algorithm that returns the shortest path to a goal node. It takes as arguments a graph (with all of the nodes and their paths), a starting node, a goal node, and a list of nodes that have been visited (initialized as empty). Here is the code:
def shortestPath(graph, start, end, visited = []):
path = str(start)
if start == end:
return path
shortest = None
for node in graph.childrenOf(start):
if str(node) not in visited:
visited = visited + [str(node)]
newPath = shortestPath(graph, start, end, visited)
if newPath = None:
continue
if shortest == None or len(newPath) < shortest:
shortest = newPath
if shortest != None:
path = path + shortest
else:
path = None
return path
I understand the concept of Depth First Search, but this recursion is boggling my mind. Let me know if my train of thought is at all on track and where I'm getting derailed.
So basically, the recursion occurs when newPath is assigned a value, which of course is a call to shortestPath. At that point, the recursion goes all the way down the graph until it either reaches a node with no children or the goal node. If it reaches a node with no children that isn't the goal, the base case is ignored, and the entire for loop is ignored, returning a value of none. That None then is simply passed up the chain of recursion. If it reaches the goal node, then the bottom layer of the recursion actually returns a value (path), which bubbles up to the top of the recursion.
At that point I get confused, because of what is going on with "shortest." So every time an actual value is returned for newPath, shortest is assigned that value, but then it is added to path. But let's say I have multiple paths to the goal node. I successfully find the first path, and path is equal to the sum of all of the newPaths/shortests. But then for the next recursion that successfully reaches the goal, doesn't path just keep adding on more newPaths? So won't the final result be a long list of every path I COULD visit to reach the goal node instead of the shortest path?
The path variable is local to the function. Every recursion call has its own stack frame - it is independent from the other calls). That means when the next call starts, then everything is brand new.

Categories