How can I get the shortest path using DFS. I've seen this question asked several times, but the replies are generally use BFS or a different algorithm. What about in the context of a robot traversing a maze? BFS isn't possible since it jumps from node to node and a robot would require backtracking.
Right now I am trying to solve the problem using:
def dfs(self, v):
v.visited = True
for adj in v.adj:
if adj.visited is False:
# set the parent
adj.successor = v
# explore the other nodes
self.dfs(adj)
However, this does not necessarily return the shortest path. Is there another way to approach this problem? I've seen some suggestions to use depth first iterative deepening, but I can't find many examples implementing this algorithm.
If anyone is interested in how I solved this here is my solution:
def dfs(self, v):
if v.successor is None: # this is the root node
v.cost = 0
v.visited = True
for adj in v.adj:
if adj.visited is False:
# set the parent
adj.successor = v
adj.cost = v.cost + 1
self.dfs(adj)
# if the cost is less switch the successor
# and set the new cost
elif adj.cost > v.cost + 1:
adj.successor = v
adj.cost = v.cost + 1
self.dfs(adj)
This is essentially a version of DFS that keeps track of the cost for each vertex. If the cost of the vertex is more than a successor + 1 step then it sets the new successor. This allows for finding the shortest path without using BFS.
Related
I am trying to get better understanding of Tarjan's algorithm for finding SCC, articulation points and bridges. I am considering a special case where the graph contains only 2 nodes with edges 0->1 and 1->0. The following code will output [0,1] as a bridge.
class Solution(object):
def criticalConnections(self, n, connections):
"""
:type n: int
:type connections: List[List[int]]
:rtype: List[List[int]]
"""
g = defaultdict(set)
pre = [-1]*n
low = [-1]*n
cnt = [0]
for c in connections:
g[c[0]].add(c[1]) # undirected graph, connect
g[c[1]].add(c[0]) # in both directions
ans = []
def dfs(edge):
v, w = edge
pre[w] = cnt[0]
low[w] = pre[w]
cnt[0] += 1
for i in g[w]:
if i == v: continue # we don't want to go back through the same path.
# if we go back is because we found another way back
if pre[i] == -1:
dfs((w,i))
# low[i] > pre[w] indicates no back edge to
# w's ancesters; otherwise, low[i] will be
# < pre[w]+1 since back edge makes low[i] smaller
if low[i] > pre[w]:
#print(low[i], pre[w]+1, (w,i))
ans.append([w,i])
low[w] = min(low[w], low[i]) # low[i] might be an ancestor of w
else: # if i was already discovered means that we found an ancestor
low[w] = min(low[w], pre[i]) # finds the ancestor with the least
# discovery time
dfs((-1,0))
return ans
print(Solution().criticalConnections(2, [[0,1],[1,0]]))
However, from many discussions online, after removing node 1, node 0 can still be considered as connected (to itself) which means edge 0->1 is not a bridge. Am I missing something here?
Or Tarjan's algorithm is not suitable for this kind of degenerate graph with 2 nodes?
A bridge in a directed graph is an edge whose deletion increases the graph's number of strongly connected components, and the number connected components when the graph is undirected. So when you remove any edge in your graph then the number of strongly connected components increases so the output of this code is correct in this case.
This post has is the result that constantly appears for this problem but doesn't provide an optimal solution.
Currently I am trying to return all shortest paths starting atfrom and ending at target using BFS but I am running into a bottleneck with either my algorithm or the data structures I use.
pseudocode:
// The graph is an adjacency list of type unordered_map<string, unordered_set<string>>
// deque with pair of (visited unordered_set, vector with current path)
deque q = [({from}, [from]);
while q:
pair = q.dequeue()
visited = pair.first
path = pair.second
foreach adjacent_node to path[-1] in the graph:
if (adjacent_node == target):
res.append(path + [adjacent_node])
else if adjacent_node not in visited:
newPath = path + [adjacent_node]
visited.add(adjacent_node)
q.push((visited, newPath))
Currently the bottleneck seems to be with the queue's pair of items. I'm unsure how to solve the problem without storing a visited set with every path, or without copying a new path into the queue.
Firstly you should know that number of shortest paths can be huge and returning them all is not practical. Consider a graph with 2k+1 layers numbered from 1 to 2k+1, in which each layer is fully connected with the next layer, and odd layers has only one point while even layers has q points. Although this graph only has k(q+1)+1 nodes and kq edges, there are in total q^k different shortest paths which can be inefficient for normal computers to handle. However if you're sure that the number of shortest paths is relatively small I can introduce the following algorithm.
The basic idea is to store a list back for each node, meaning the shortest distance between from and x equals to the shortest distance between from and v plus one if and only if v in back[x]. back[x] can be computed during the process. Then you can perform a depth-first search to print all the shortest path. Pseudo code (BTW I noticed that your code is not correct):
queue q = [ from ]
visited = set<node>
back = map<node, list<node>>
while q.not_empty():
now = q.front()
if (now == target):
continue
foreach adjacent_node to now in the graph:
if (adjacent_node in visited):
back[adjacent_node].push(now)
else:
visited.add(adjacent_node)
back[adjacent_node] = [ now ]
q.push(adjacent_node)
# Now collect all shortest paths
ret = []
current = []
def collect(x):
current.push(x)
if (x == from):
ret.push(current.reversed())
return
foreach v in back[x]:
collect(v)
current.pop()
Sorry for my poor English. Feel free to point out my typos and mistakes.
I have a graph and I want to find all shortest paths between two nodes. I've found a shortest path between two nodes by BFS. However, it just gives me one of the shortest paths if there exists one more than.
How could I get all of them using BFS?
I've implement my code from well-known BFS pseudocode.
Also, I have a adjacency list vector which holds adjacency vertices for all nodes.
You can easily do it by maintaining a list or vector of parents for each node.
If two or more nodes ( say X, Y, Z) at the same distance from the starting node , leads to another node M , make all X , Y and Z as the parents of M.
You just have to add a check to see while adding a parent to the node whether that parent is in the same level as the previous parents.
By level , I mean the distance from the starting point.
This way you can get all the shortest paths by tracing back the parent vectors.
Below is my C++ implementation.
I hope you know how to print the paths by starting from the destination ,tracing the parents and reach the starting point.
EDIT : Pseudo Code
bfs (start , end)
enqueue(start)
visited[start] = 1
while queue is NOT empty
currentNode = queue.front()
dequeue()
if(currentNode == end)
break
for each node adjacent to currentNode
if node is unvisited
visited[node] = visited[curr] + 1
enqueue(node)
parent[node].add(currentNode)
else if(currentNode is in same level as node's parents)
parent[node].add(currentNode)
return
If the graph is large, finding all paths from start to end and then selecting the shortest ones can be very inefficient. Here is a better algorithm:
Using BFS, label each node with its distance from the start node. Stop when you get to the end node.
def bfs_label(start, end):
depth = {start: 0}
nodes = [start]
while nodes:
next_nodes = []
for node in nodes:
if node == end:
return depth
for neighbor in neighbors(node):
if neighbor not in depth:
depth[neighbor] = depth[node] + 1
fringe.append(neighbor)
Using DFS, find all paths from the start node to the end node such that the depth strictly increases for each step of the path.
def shortest_paths(node, end, depth, path=None):
if path is None:
path = []
path.append(node)
if node == end:
yield tuple(path)
else:
for neighbor in neighbors(node):
if neighbor in depth and depth[neighbor] == depth[node]+1:
for sp in shortest_paths(neighbor, end, depth, path):
yield sp
path.pop()
A simpler way is to find all paths from source to destination using dfs. Now find the shortest paths among these paths. Here is a sudo code:
dfs(p,len)
if(visited[p])
return
if(p== destination)
paths.append(len)
return
visited[p]=1
for each w adjacent to p
dfs(w,len+1)
visited[p]=0
You can find the path by maintaining an array for paths. I will leave that to you as an assignment
We can use a simple BFS algorithm for finding all the shortest paths. We can maintain the path along with the current node. I have provided the link to the python code for the same below.
https://gist.github.com/mridul111998/c24fbdb46492b57f7f17decd8802eac2
I know that myself,and many others probably stuch here,
Well,according to the CLRS(3 edition,22.4.2),there is a O(n) algorithm for finding all simple paths between 2 nodes in a directed acyclic graph.
I went through similar questions,Number of paths between two nodes in a DAG and All the paths between 2 nodes in graph,but on both occasions,no proper explanation or pseudocode is mentioned,or if it is,i doubt that is it the most efficient one (O(n)).
If someone could really post one exact code,or pseudocode,which settles the deal,because as i went through all those above links,i didnt really find 1 single answer which stands Tall.
It would be better if the code also handles cyclic graphs,i.e,IF there is a cycle in the graph,but If no path between two nodes contains the cycle,the number of paths SHOULD be FINITE,else INFINITE.
Jeremiah Willcock's answer is correct but light on details. Here's the linear-time algorithm for DAGs.
for each node v, initialize num_paths[v] = 0
let t be the destination and set num_paths[t] = 1
for each node v in reverse topological order (sinks before sources):
for each successor w of v:
set num_paths[v] = num_paths[v] + num_paths[w]
let s be the origin and return num_paths[s]
I'm pretty sure the problem for general directed graphs is #P-complete, but I couldn't find anything in a couple minutes of searching except an unsourced question on cstheory.
Okay, here's some pseudocode. I've integrated the contents of the previous algorithm with the topological sort and added some cycle detection logic. In case of a cycle between s and t, the values of num_paths may be inaccurate but will be zero-nonzero depending on whether t is reachable. Not every node in a cycle will have in_cycle set to true, but every SCC root (in the sense of Tarjan's SCC algorithm) will, which suffices to trigger the early exit if and only if there is a cycle between s and t.
REVISED ALGORITHM
let the origin be s
let the destination be t
for each node v, initialize
color[v] = WHITE
num_paths[v] = 0
in_cycle[v] = FALSE
num_paths[t] = 1
let P be an empty stack
push (ENTER, s) onto P
while P is not empty:
pop (op, v) from P
if op == ENTER:
if color[v] == WHITE:
color[v] = GRAY
push (LEAVE, v) onto P
for each successor w of v:
push (ENTER, w) onto P
else if color[v] == GRAY:
in_cycle[v] = TRUE
else: # op == LEAVE
color[v] = BLACK
for each successor w of v:
set num_paths[v] = num_paths[v] + num_paths[w]
if num_paths[v] > 0 and in_cycle[v]:
return infinity
return num_paths[s]
I'm working on better understanding the application of a depth-first search algorithm. I understand how to use it to traverse a binary search tree to produce a sorted list. My Python implementation looks like this:
class bst_node:
def __init__(self, x):
self.x = x
self.left = None
self.right = None
def get_dfs_path(bst_node):
""" Returns a depth-first search path for a BST """
left = [] if bst_node.left == None else get_dfs_path(bst_node.left)
right = [] if bst_node.right == None else get_dfs_path(bst_node.right)
return left + [bst_node] + right
Which works quite nicely. I'm struggling to understand, however, whether this algorithm can be meaningfully applied to a digraph in general, rather than the more strict BST. Consider the following digraph node implementation:
class di_node:
def __init__(self, x):
self.x = x
self.visited = False
self.children = []
Since a node in a digraph can have an arbitrary number of children, the dfs logic can't simply construct the path as dfs_path(left) + parent_node + dfs_path(right). Can someone help me understand if/how dfs applies to a digraph?
EDIT
Ok, based on the responses let me attempt a dfs traversal for a di_node. Please let me know if I'm anywhere close to the mark:
def get_dfs_path(di_node):
""" Returns a depth-first search path for a digraph """
if di_node.visited:
return []
else:
di_node.visited = True
return [di_node] + [ get_dfs_path(child) for child in di_node.children) ]
As you noticed in-order traversal (left-subtree, current node, right subtree) doesn't make much sense for general graphs since a node can have more than two subtrees. However a depth first search can also use pre-order (process the current node first, then the subtrees) or post-order (first process the subtrees, then the current node) traversal. Those two work just fine with graphs.
One thing you have to keep track of when performing DFS on graphs is which nodes you already visited. Otherwise you'd get infinite loops when traversing cyclic graphs.
You can use DFS in a graph to detect cycles. Keep track of visited nodes and if you visit a node that was already visited then you have detected a cycle.