Shortest path Graph BFS python - python

Trying to return the int for shortest path in a graph, using BFS. The idea is to use a q, append into the q as [node,distance] and then when we traverse increase distance and keep track of the count and when we hit our destination first time that means we found shortest path so we return that. But I got error " currNode,distance = q.popleft()
ValueError: not enough values to unpack (expected 2, got 1)"
def shortestPath(graph,nodeA,nodeB):
q = deque((nodeA,0))
visited = set(nodeA)
while q:
currNode,distance = q.popleft()
if currNode == nodeB:
return distance
for neighbor in graph[currNode]:
if neighbor not in visited:
visited.add(neighbor)
q.append([neighbor,distance+1])
return -1
graph_three = {
'w':['x','v'],
'x':['w','y'],
'y':['x','z'],
'z':['y','v'],
'v':['z','w']
}
print(shortestPath(graph_three,'w','z'))

Deque takes an iterable of elements as input, you gave it a tuple so your deque will contains two elements instead of the expected one tuple of two elements.
fix line 2 into:
q = deque([(nodeA,0)])
also here is a cleaner implementation of BFS:
def shortestPath(graph, root, target):
if root == target: return 0
q = collections.deque(root)
visited = set(root)
distance = 0
while q:
for _ in range(len(q)):
node = q.popleft()
for neighbor in graph[node]:
if neighbor == target:
return distance + 1
elif neighbor not in visited:
visited.add(neighbor)
q.append(neighbor)
distance += 1
return -1

Related

How do I return the length of the path found by Breadth First Search?

I'm trying to augment the typical BFS algorithm to also return the length of the path it found. Here's what I've written so far:
from collections import deque
length = 1
visited = set()
q = deque()
visited.add("Start")
while q:
v = q.popleft()
length += 1
if v == "End":
return length
else:
for neighbor in graph[v]:
if neighbor not in visited:
visited.add(neighbor)
q.append(neighbor)
return 0
Here, I'm assuming a graph of strings. An example graph could be
graph = { "Start" : ["A"],
"A" : ["B"],
"B" : ["End"],
"End" : []
}
I understand that this is wrong as it will count the entire size of graph and not just the length of the path to the goal. How can I modify it to return the length of the path to the goal it found?
Based on the advice of #Karl Knechtel, I was able to write it!
from collections import deque
visited = set()
q = deque([("Start", 0)])
visited.add("Start")
while q:
v, length = q.popleft()
if v == "End":
return length + 1
else:
for neighbor in graph[v]:
if neighbor not in visited:
visited.add(neighbor)
q.append((neighbor, length + 1))
return 0

Converting BFS to Djikstra to find the shortest path

How can I convert the following BFS algorithm to find the shortest path using Djikstra? I know that I need to update distances of neighbors, but I am confused on how exactly to extend the following BFS with it. The constraint is we can move only along L shaped paths between two nodes.
from collections import deque
N = 8
board_p = [[(-1,-1) for f in range(0,N)] for i in range(0,N)]
def Adjacents(u):
adj = []
for e in [(-2,-1),(-2,1),(2,1),(2,-1),(-1,-2),(1,-2),(-1,2),(1,2)]:
v = (u[0] + e[0], u[1] + e[1])
if v[0] >= 0 and v[0] < N and v[1] >= 0 and v[1] < N: adj.append(v)
return adj;
def Moves(s,t):
q = deque()
q.append(s)
board_p[s[0]][s[1]] = s # "root" of BFS-traversal points to it self (avoid loop over "back-edge" to s)
while q:
u = q.popleft()
if u == t: break
for v in Adjacents(u):
if board_p[v[0]][v[1]] == (-1,-1):
board_p[v[0]][v[1]] = u
q.append(v)
# walk the path back (using parent "pointers")
path = [(t)]
while t != s:
t = board_p[t[0]][t[1]]
path.append(t)
path.reverse()
return path
print(Moves((1,1),(5,5)))

Set list next to None

When I try to make the next attribute of a node, p, of a linked list point to None, I use p.next = None. But what if I want to make the node corresponding to p.next to None?
An example would be when try to rotate a linked list, which ends with a node's next equal to None, I want to make the new list's last element's next point to None but I think I keep deleting the element that it pointed to.
Here's my code for rotating the list by k positions. If you want to see the full description of the problem see here
def rotate(head, k):
'''
head is pointer to the head, k is the number of positions to rotate
'''
if not head or k == 0:
return head
p = head
d = head
counter = 1
while p.next != None:
counter += 1
p = p.next
out = ListNode(0)
if k % counter == 0:
return head
if counter < k:
counter = counter % k
for _ in range(counter):
p.next = d
d = d.next
p = p.next
out = p
d.next.next = None
return out
Sounds like you want to take the last k values and append them to the front.
p.next is the next node. In general when we want to change p's next we need to grab temp = p.next p.next = newNode and then we can continue.
In this case though, I'd find the length of the list, set tail.next = head, subtract k (accounting for wraparound), then walk forward till N-k, and set that node's p.next = None
Something like:
p, len, prev = head, 0, None
while p:
prev = p
p = p.next
len += 1
# prev is tail, set it's next to head
prev.next = head
# find the node to detach
p = head
for i in xrange(len):
p = p.next
p.next = None
You'll need to figure out corner cases

Nearest Neighbour - Error code iterable in Python

Here is the code I'm using and getting an 'int' object is not iterable error and Idon't know how to fix it.
def shortestpath(graph,start,end,visited=[],distances={},predecessors={}):
"""Find the shortest path between start and end point of a list"""
# detect if it's the first time through, set current distance to zero
if not visited: distances[start]=0
if start==end:
# we've found our end point, now find the path to it, and return
path=[]
while end != None:
path.append(end)
end=predecessors.get(end,None)
return distances[start], path[::-1]
# process neighbors as per algorithm, keep track of predecessors
for neighbor in graph[start]:
if neighbor not in visited:
neighbordist = distances.get(neighbor,sys.maxint)
tentativedist = distances[start] + graph[start][neighbor]
return tentativedist
if tentativedist < neighbordist:
distances[neighbor] = tentativedist
predecessors[neighbor]=start
# neighbors processed, now mark the current point as visited
visited.append(start)
# finds the closest unvisited node to the start
unvisiteds = dict((k, distances.get(k,sys.maxint)) for k in graph if k not
in visited)
closest = min(unvisiteds, key=unvisiteds.get)
# now we can take the closest point and recurse, making it current
return shortestpath(graph,closest,end,visited,distances,predecessors)
#main
graph=[0,8,7,5,2,10]
n=len(graph)
start=graph[0]
end=graph[n-1]
print shortestpath(graph,start,end)
Since you have graph=[0,8,7,5,2,10], graph[start] is an integer therefore you got the error in the for-loop:
for neighbor in graph[start]:
and I don't think you can use graph[start][neighbor] in line: tentativedist = distances[start] + graph[start][neighbor]

Depth of a tree using DFS

I'm trying to write code that returns the depth of the deepest leaf in a tree with arbitrary number of children per nodes, in Python, using DFS rather than BFS. It seeems I'm close, but the following code still has some bug that I can't figure out (i.e. the returned depth is not correct). Any help?
A test tree would be simply: [[1,2,3],[4,5],[6],[7],[8],[],[],[],[]]
def max_depth_dfs(tree): # DOESN'T WORK
max_depth, curr_depth, Q = 0,0, [0]
visited = set()
while Q != []:
n = Q[0]
more = [v for v in tree[n] if v not in visited]
if not more:
visited.add(n)
curr_depth -= 1
Q = Q[1:]
else:
curr_depth += 1
max_depth = max(max_depth, curr_depth)
Q = more + Q
return max_depth
I found the bug!
if not more:
visited.add(n)
curr_depth -= 1
Q = Q[1:]
When you visit the node 4, curr_depth is equal to 2. Node 4 has no children, so you decrease the curr_depth and curr_depth is equal to 1 now. However, the next node you will visit is node 5 and the depth of node 5 is 2 instead of 1. Therefore, curr_depth doesn't record the correct depth of the node in the tree.
The following solution may be helpful.
def max_depth_dfs(tree):
max_depth, curr_depth, Q = 0, 0, [0]
visited = set()
while Q != []:
n = Q[0]
max_depth = max(max_depth, curr_depth)
if n in visited:
curr_depth -= 1
Q = Q[1:]
continue
#print n, curr_depth #show the node and its depth in the tree
visited.add(n)
more = [v for v in tree[n]]
if not more:
Q = Q[1:]
else:
curr_depth += 1
Q = more + Q
return max_depth
I used try .. catch to distinguish branches from leafs. update No more exceptions :)
from collections import Iterable
tree = [[1,2,3],[4,5, [1, 6]],[6],[7],[8],[],[],[],[]]
def max_depth(tree, level=0):
if isinstance(tree, Iterable):
return max([ max_depth(item, level+1) for item in tree])
else: # leaf
return level
print max_depth(tree)
Here is the non-recurison version:
from collections import Iterable
def max_depth_no_recur(tree):
max_depth, node = 0, iter(tree)
stack = [node]
while stack:
try:
n = node.next()
except StopIteration:
if len(stack) > max_depth:
max_depth = len(stack)
node = stack.pop()
continue
if isinstance(n, Iterable):
stack.append(node)
node = iter(n)
return max_depth
After taking into account all the good feedback I got from Alex and Adonis and refining the code, I currently have the current version:
def max_depth_dfs(tree): # correct
max_depth, curr_depth, Q = 0, 0, [0]
visited = set()
while Q != []:
n = Q[0]
if n in visited:
Q = Q[1:]
curr_depth -= 1
visited.remove(n) # won't go back, save memory
print 'backtrack from', n
continue
# proper place to print depth in sync with node id
print 'visiting', n, 'children=', tree[n], 'curr_depth=', curr_depth, 'Q=', Q,
print visited # only current path, instead of visited part of tree
if tree[n]:
visited.add(n) # if leaf, won't ever try to revisit
Q = tree[n] + Q
curr_depth += 1
max_depth = max(max_depth, curr_depth) # no need to check if depth decreases
else:
Q = Q[1:] # leaf: won't revisit, will go to peer, if any, so don't change depth
print 'no children for', n
return max_depth

Categories