How to determine last stack space in recursion - python

I'm implementing well-known depth first search by recursion. I wonder whether there may be a way to know the code within last stack space. Why I need is I don't want to put -> character at the end of output. If possible just '\n' in the last step.
def DFS(self, vertex=None, visited=None):
if vertex is None:
vertex = self.root
if visited is None:
visited = []
print(f"{vertex} -> ", end='')
visited.append(vertex)
for neighbor in self.getNeighbors(vertex):
if neighbor not in visited:
visited.append(neighbor)
print(f"{neighbor} -> ", end='')
self.DFS(neighbor, visited)
For example, it yields 1 -> 2 -> 4 -> 5 ->
Is there anyway to do within the same method? Moreover, I could write a helper function removing the last -> character.
#Edit: What I've done according to #Carcigenicate's comment follows
return visited # last line in DFS method
-- in main --
dfs = graph.DFS()
path = " -> ".join(str(vertex) for vertex in dfs)
print(path)

Rather than trying to special-case the last vertex, special-case the first. That is, don't try to figure out when not to append the "->", just don't do it for the first vertex:
def DFS(self, vertex=None, visited=None):
if vertex is None:
vertex = self.root
else:
# Not the first vertex, so need to add the separator.
print(f" ->", end='')
if visited is None:
visited = []
print(f"{vertex}", end='')
visited.append(vertex)
for neighbor in self.getNeighbors(vertex):
if neighbor not in visited:
# no need to append here, because it will be done in the recursive call.
# and the vertex will be printed in the recursive call, too.
# visited.append(neighbor)
# print(f"{neighbor} -> ", end='')
self.DFS(neighbor, visited)
This assumes that your initial call will always be DFS(root, None, visited). Which I think is a reasonable assumption.
On second thought, perhaps using the visited parameter as the condition is a better idea:
if vertex is None:
vertex = self.root
if visited is None:
visited = []
else:
# Not the first vertex, so need to add the separator.
print(f" ->", end='')
print(f"{vertex}", end='')
The whole point is that it's easier to special-case the first item than the last.

Related

Graph recursive DFS when cycles exist?

I'm interested in handling DFS in undirected (or directed) graphs where cycles exist, such that the risk of entering an infinite-loop is non-trivial.
Note: This question is not about the cycle-detection problem(s) on LeetCode. Below is an iterative approach:
g = {'a':['b','c'],
'b':['a','f'],
'c':['a','f','d'],
'd':['c','e'],
'e':['d'],
'f':['c','b'],
'g':['h'],
'h':['g']
}
def dfs(graph, node, destination):
stack = [node]
visited = []
while stack:
current = stack.pop()
if current == destination:
return True
visited.append(current)
next_nodes = list(filter(lambda x: x not in visited + stack, graph[current]))
stack.extend(next_nodes)
return False
dfs(g,'h', 'g')
>>> True
dfs(g,'a', 'g')
>>> False
My question is, does such a recursive approach exist? And if so, how can it be defined in python?
If you're not interested in detecting if there are any loops or not and just interested in avoiding infinite loops (if any), then something like the following recursive implementation would work for you:
def dfs(graph, node, destination, visited=None):
if visited is None:
visited = set()
if node == destination:
return True
visited.add(node)
return any(
dfs(graph, neighbor, destination, visited=visited)
for neighbor in graph[node]
if neighbor not in visited
)
Note that a generator expression is used inside any, so it's evaluated in a lazy manner (one by one), and the whole any(...) expression returns True early as soon as a solution (i.e. a path to the destination) is found without checking the other neighbors and paths, so no extra recursive calls are made.

Leetcode 261 Graph Valid Tree: second test fails

I am trying to write a solution for Leet Code problem 261. Graph Valid Tree:
Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.
Example 1:
Input: n = 5, and edges = [[0,1], [0,2], [0,3], [1,4]]
Output: true
Example 2:
Input: n = 5, and edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
Output: false
Here is my solution thus far. I believe that the goal is to detect cycles in the tree. I use dfs to do this.
class Node:
def __init__(self, val):
self.val = val
self.outgoing = []
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
visited = {}
for pre, end in edges:
if pre not in visited:
"we add a new node to the visited set"
visited[pre] = Node(pre)
if end not in visited:
visited[end] = Node(end)
"We append it to the list"
visited[pre].outgoing.append(visited[end])
def dfs(current, dvisit = set()):
if current.val in dvisit:
print("is the condition happening here")
return True
dvisit.add(current.val)
for nodes in current.outgoing:
dfs(nodes, dvisit)
return False
mdict = set()
for key in visited.keys():
mdict.clear()
if dfs(visited[key], mdict) == True:
return False
return True
It fails this test n = 5, edges = [[0,1],[1,2],[2,3],[1,3],[1,4]]
It is supposed to return false but it returns true.
I placed some print statements in my dfs helper function and it does seem to be hitting the case where dfs is supposed to return true. However for some reason, the case in my for loop does not hit in the end, which causes the entire problem to return true for some reason. Can I receive some guidance on how I can modify this?
A few issues:
The given graph is undirected, so edges should be added in both directions when the tree data structure is built. Without doing this, you might miss cycles.
Once edges are made undirected, the algorithm should not travel back along the edge it just came from. For this purpose keep track of the parent node that the traversal just came from.
In dfs the returned value from the recursive call is ignored. It should not: when the returned value indicates there is a cycle, the loop should be exited and the same indication should be returned to the caller.
The main loop should not clear mdict. In fact, if after the first call to dfs, that loop finds another node that has not been visited, then this means the graph is not a tree: in a tree every pair of nodes is connected. No second call of dfs needs to be made from the main code, which means the main code does not need a loop. It can just call dfs on any node and then check that all nodes were visited by that call.
The function could do a preliminary "sanity" check, since a tree always has one less edge than it has vertices. If that is not true, then there is no need to continue: it is not a tree.
One boundary check could be made: when n is 1, and thus there are no edges, then there is nothing to call dfs on. In that case we can just return True, as this is a valid boundary case.
So a correction could look like this:
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
if n != len(edges) + 1: # Quick sanity check
return False
if n == 1: # Boundary case
return True
visited = {}
for pre, end in edges:
if pre not in visited:
visited[pre] = Node(pre)
if end not in visited:
visited[end] = Node(end)
visited[pre].outgoing.append(visited[end])
visited[end].outgoing.append(visited[pre]) # It's undirected
def dfs(parent, current, dvisit):
if current.val in dvisit:
return True # Cycle detected!
dvisit.add(current.val)
for node in current.outgoing:
# Avoid going back along the same edge, and
# check the returned boolean!
if node is not parent and dfs(current, node, dvisit):
return True # Quit as soon as cycle is found
return False
mdict = set()
# Start in any node:
if dfs(None, visited[pre], mdict):
return False # Cycle detected!
# After one DFS traversal there should not be any node that has not been visited
return len(mdict) == n
A tree is a special undirected graph. It satisfies two properties
It is connected
It has no cycle.
No cycle can be expressed as NumberOfNodes ==NumberOfEdges+1.
Based on this, given edges:
1- Create the graph
2- then traverse the graph and store the nodes in a set
3- Finally check if two conditions above are met
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
from collections import defaultdict
graph = defaultdict(list)
for src, dest in edges:
graph[src].append(dest)
graph[dest].append(src)
visited = set()
def dfs(root):
visited.add(root)
for node in graph[root]:
if node in visited:
# if you already visited before, means you alredy run dfs so do not run dfs again
continue
dfs(node)
dfs(0)
# this shows we have no cycle and connected
return len(visited) == n and len(edges)+1 == n
This question is locked in leetcode but you can test it here for now:
https://www.lintcode.com/problem/178/description

Iterative postorder traversal of a binary tree with a single stack, how to approach the problem?

I have been studying up on algorithms and data structures and I wrote a post-order traversal for a binary tree without using recursion and using only one stack.
Here is the code:
def postorder_iterative(self):
current = self
s = []
current1 = None
done = 0
def peek(s):
return s[-1]
while(not done):
if current and (current != current1):
s.append(current)
current = current.leftChild
elif(len(s) > 0):
if peek(s).rightChild and peek(s).rightChild != current:
current = peek(s).rightChild
else:
current = current1 = s.pop()
print(current.key)
else:
done = 1
This code actually works but it took me forever to come up with it.
Can someone explain what is the intuitive way of thinking about this problem?
I'd like to be able to reproduce it using logic and not spend as much time as I did on it.
Post-order traversal requires that you only print the current node value after traversing both the left and right subtrees. You are using the stack to traverse the left tree only, and use the current1 variable (the last node printed) to know that you are now backing out of a right-hand side tree so you can print the current node.
I'd rename current to node, current1 to last (for last printed), remove the peek() function to just reference stack[-1] directly as tos (top of stack), and simplify your approach to:
def postorder_iterative(self):
node, last = self, None
stack = []
while True:
if node and node is not last:
# build up the stack from the left tree
stack.append(node)
node = node.leftChild
elif stack:
# no more left-hand tree to process, is there a right-hand tree?
tos = stack[-1]
if tos.rightChild and tos.rightChild is not node:
node = tos.rightChild
else:
# both left and right have been printed
node = last = stack.pop()
print(last.key)
else:
break
It is still hard to follow what is going on however, as the connection between last and the point where the left and right subtrees have been processed isn't all that clear.
I'd use a single stack with a state flag to track where in the process you are:
def postorder_iterative(self):
new, left_done, right_done = range(3) # status of node
stack = [[self, new]] # node, status
while stack:
node, status = stack[-1]
if status == right_done:
stack.pop()
print(node.key)
else:
stack[-1][1] += 1 # from new -> left_done and left_done -> right_done
# new -> add left child, left_done -> add right child
next_node = [node.leftChild, node.rightChild][status]
if next_node is not None:
stack.append((next_node, new))
Nodes go through three states, simply by incrementing the state flag. They start as new nodes, then progress to left, then right, and when the top of the stack is in that last state we remove it from the stack and print the node value.
When still in the new or left states, we add the left or right node, if present, to the stack as a new node.
Another approach pushes the right-hand tree onto the stack before the current node. Then later, when you return to the current node, having taken it from the stack, you can detect the case where you still need to process the right-hand side because the top of the stack will have the right-hand node. In that case you swap the top of the stack with the current node and continue from there; you'll later return to the same place and will no longer have that right-hand side node on the top of the stack so you can print:
def postorder_iterative(self):
stack = []
node = self
while node or stack:
while node:
# traverse to the left, but add the right to the stack first
if node.rightChild is not None:
stack.append(node.rightChild)
stack.append(node)
node = node.leftChild
# left-hand tree traversed, time to process right or print
node = stack.pop()
if stack and node.rightChild is stack[-1]:
# right-hand tree present and not yet done, swap tos and node
node, stack[-1] = stack[-1], node
else:
print(node.key)
node = None

Uniform Cost Search in Python

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.
# Node data structure
class Node:
def __init__(self, label):
self.out_edges = []
self.label = label
self.is_goal = False
def add_edge(self, node, weight = 0):
self.out_edges.append(Edge(node, weight))
# Edge data structure
class Edge:
def __init__(self, node, weight = 0):
self.node = node
self.weight = weight
def to(self):
return self.node
# Graph data structure, utilises classes Node and Edge
class Graph:
def __init__(self):
self.nodes = []
# some other functions here populate the graph, and randomly select three goal nodes.
Now I am trying to implement a uniform-cost search (i.e. a BFS with a priority queue, guaranteeing a shortest path) which starts from a given node v, and returns a shortest path (in list form) to one of three goal node. By a goal node, I mean a node with the attribute is_goal set to true.
This is my implementation:
def ucs(G, v):
visited = set() # set of visited nodes
visited.add(v) # mark the starting vertex as visited
q = queue.PriorityQueue() # we store vertices in the (priority) queue as tuples with cumulative cost
q.put((0, v)) # add the starting node, this has zero *cumulative* cost
goal_node = None # this will be set as the goal node if one is found
parents = {v:None} # this dictionary contains the parent of each node, necessary for path construction
while not q.empty(): # while the queue is nonempty
dequeued_item = q.get()
current_node = dequeued_item[1] # get node at top of queue
current_node_priority = dequeued_item[0] # get the cumulative priority for later
if current_node.is_goal: # if the current node is the goal
path_to_goal = [current_node] # the path to the goal ends with the current node (obviously)
prev_node = current_node # set the previous node to be the current node (this will changed with each iteration)
while prev_node != v: # go back up the path using parents, and add to path
parent = parents[prev_node]
path_to_goal.append(parent)
prev_node = parent
path_to_goal.reverse() # reverse the path
return path_to_goal # return it
else:
for edge in current_node.out_edges: # otherwise, for each adjacent node
child = edge.to() # (avoid calling .to() in future)
if child not in visited: # if it is not visited
visited.add(child) # mark it as visited
parents[child] = current_node # set the current node as the parent of child
q.put((current_node_priority + edge.weight, child)) # and enqueue it with *cumulative* priority
Now, after lots of testing and comparing with other alogrithms, this implementation seemed to work pretty well - up until I tried it with this graph:
For whatever reason, ucs(G,v) returned the path H -> I which costs 0.87, as opposed to the path H -> F -> I, costing 0.71 (this path was obtained by running a DFS). The following graph also gave an incorrect path:
The algorithm gave G -> F instead of G -> E -> F, obtained again by the DFS. The only pattern I can observe among these rare cases is the fact that the chosen goal node always has a loop. I can't figure out what is going wrong though. Any tips will be much appreciated.
Usually for searches, I tend to keep the path to a node part of the queue. This is not really memory efficient, but cheaper to implement.
If you want the parent map, remember that it is only safe to update the parent map when the child is on top of the queue. Only then has the algorithm determined the shortest path to the current node.
def ucs(G, v):
visited = set() # set of visited nodes
q = queue.PriorityQueue() # we store vertices in the (priority) queue as tuples
# (f, n, path), with
# f: the cumulative cost,
# n: the current node,
# path: the path that led to the expansion of the current node
q.put((0, v, [v])) # add the starting node, this has zero *cumulative* cost
# and it's path contains only itself.
while not q.empty(): # while the queue is nonempty
f, current_node, path = q.get()
visited.add(current_node) # mark node visited on expansion,
# only now we know we are on the cheapest path to
# the current node.
if current_node.is_goal: # if the current node is a goal
return path # return its path
else:
for edge in in current_node.out_edges:
child = edge.to()
if child not in visited:
q.put((current_node_priority + edge.weight, child, path + [child]))
Note: I haven't really tested this, so feel free to comment, if it doesn't work right away.
A simple check before expanding the node can save you duplicate visits.
while not q.empty(): # while the queue is nonempty
f, current_node, path = q.get()
if current_node not in visited: # check to avoid duplicate expansions
visited.add(current_node) # mark node visited on expansion,
# only now we know we are on the cheapest path to
# the current node.
if current_node.is_goal: # if the current node is a goal
return path # return its path
...

Returning value when condition met in recursion

I am trying to find tours in a graph. I have written the following code, this seems to be printing tours correctly. I want it to stop once it have found the first tour and return the tour as a list. However, the recursion stack seems to finish to completion and I am not getting the desired result. How can I return a value and fully stop the recursion when I find the first tour i.e. my condition is met? Thanks.
def get_tour(start, graph, path):
if path==[]:
from_node=start
else:
from_node=path[-1][1]
if graph==[]:
if start in path[-1]:
print "Tour Found"
return path
else:
edges=[node for node in graph if from_node in node]
for edge in edges:
to_node=[i for i in edge if i<> from_node][0]
p=list(path)
p.append((from_node,to_node))
g=list(graph)
g.remove(edge)
get_tour(start, g,p)
g=[(1,2), (1,3), (2,3)]
get_tour(1, graph=g, path=[])
When using recursion you need to pass back the return value up to the whole call stack. Normally this isn't the best way to use recursion.
Without going in the details of your code, here is a quick suggestion:
def get_tour(start, graph, path):
ret_val = None
# Some code..
if graph==[]:
# Some code..
else:
edges=[node for node in graph if from_node in node]
for edge in edges:
# Some more code..
ret_val = get_tour(start, g,p)
if ret_val:
break
return ret_val
The reason the code continues to execute after finding the tour and returning the path is because it returns it to the call that was made within the iteration through the edges. If there is no break or return condition there then the iterations continue (and more recursive calls are followed).
Here is an amended version of your code that returns to the original call (as well as the recursive call) as soon as the conditions are satisfied, I have added some debug information to try to make the process clearer:
#!/usr/bin/python
# globals
verbose = True
def get_tour(start, graph, path):
if path==[]:
from_node=start
else:
from_node=path[-1][1]
if verbose:
print '\nfrom_node:\t', from_node
print 'start:\t', start
print 'graph:\t', graph
print 'path:\t', path
if graph==[]:
if start in path[-1]:
print "Tour Found"
return path
else:
edges=[node for node in graph if from_node in node]
for edge in edges:
to_node=[i for i in edge if i <> from_node][0]
p=list(path)
p.append((from_node,to_node))
g=list(graph)
g.remove(edge)
path = get_tour(start, g, p)
if path:
return path
g=[(1,2), (1,3), (2,3)]
get_tour(1, graph=g, path=[])

Categories

Resources