So I have been trying to create a graph program in python with classes that has dfs, bfs and dijkstra's algorithmn implemented within it and so far have come up with:
class Vertex:
def __init__(self, name):
self.name = name
self.connections = {}
def addNeighbour(self, neighbour, cost):
self.connections[neighbour] = cost
class Graph:
def __init__(self):
self.vertexs = {}
def addVertex(self, newVertex):
new = Vertex(newVertex)
self.vertexs[newVertex] = new
def addEdge(self, src, dest, cost):
self.vertexs[src].addNeighbour(self.vertexs[dest], cost)
def dfs(self, start, end, visited):
visited[start] = True
print(start, end=' ')
if start == end:
# End node found
return True
else:
# Use depth first search
for connection in graph.vertexs[start].connections:
if visited[connection.name] == False:
if self.dfs(connection.name, end, visited) == True:
# Return true to stop extra nodes from being searched
return True
def bfs(self, start, end, visited, queue):
if len(queue) == 0:
# Queue is empty
queue.append(start)
visited[start] = True
currentNode = queue.pop(0)
print(currentNode, end=' ')
if start == end:
# End node found
return True
else:
# Do breadth first search
for connection in graph.vertexs[currentNode].connections:
if visited[connection.name] == False:
# Queue all its unvisited neighbours
queue.append(connection.name)
for newNode in queue:
self.bfs(newNode, end, visited, queue)
def dijkstra(self, current, currentDistance, distances, visited, unvisited):
for neighbour, distance in distances.items():
if neighbour.name not in unvisited:
continue
newDistance = currentDistance + distance
if unvisited[neighbour.name] is None or unvisited[neighbour.name] > newDistance:
unvisited[neighbour.name] = newDistance
visited[current] = currentDistance
del unvisited[current]
if not unvisited:
return True
candidates = [node for node in unvisited.items() if node[1]]
current, currentDistance = sorted(candidates)[0]
self.dijkstra(current, currentDistance, graph.vertexs[current].connections, visited, unvisited)
return visited
def setup():
graphList = {
# Node number: [destination number, cost]
0: {4: 6, 6: 1},
1: {6: 2},
2: {0: 9, 1: 4, 3: 3},
3: {4: 7},
4: {1: 3, 5: 5},
5: {0: 2, 1: 6, 4: 3},
6: {2: 4, 3: 6}
}
graph = Graph()
for i in range(len(graphList)):
graph.addVertex(i)
for dictLength in range(len(graphList)):
for key in list(graphList[dictLength].keys()):
graph.addEdge(dictLength, key, graphList[dictLength][key])
return graph, graphList
graph, graphList = setup()
print("DFS travsersal path from node 1 to node 0:")
graph.dfs(1, 0, [False] * len(graphList))
print()
print("BFS traversal path from node 1 to node 0:")
graph.bfs(1, 0, [False] * len(graphList), [])
print()
print("Shortest possible path from node 1 to 0:")
result = graph.dijkstra(1, 0, graph.vertexs[2].connections, {}, {node: None for node in graphList})
cost = result[len(result) - 1]
path = " ".join([str(arrInt) for arrInt in list(result.keys())])
print(path, "costing", cost)
But there seems to be a problem with the output I think. If I wanted to travel from node 1 to node 0, the current output is:
DFS travsersal path from node 1 to node 0: 1 6 2 0 BFS traversal path from node 1 to node 0: 1 6 2 3 0 3 4 5 Shortest possible path from node 1 to 0: 1 0 3 4 5 6 2 costing 10
However, I think the output should be:
DFS travsersal path from node 1 to node 0: 1 6 2 0 4 5 3 BFS traversal path from node 1 to node 0: 1 6 2 3 0 4 5 Shortest possible path from node 1 to 0: 1 6 2 0 costing 15
Can anyone see any problems with this?
Thanks!
There are actually several issues in your code:
You need to specify to your Djikstra's algorithm where to stop, in your code it is not mentioned what is the end node (in your example it should be 0)
Computing the cost as cost = result[len(result) - 1]
does not get you the last element in a dictionary (dictionaries are usually not ordered, so the "last element" does not even exist!). You should retrieve the cost as cost = result[end]
, where end
is the final node, 0 in your example.
You are calling the function as result = graph.dijkstra(1, 0, graph.vertexs[2].connections, {}, {node: None for node in graphList})
, however, the third argument of this function should be the set of neighbours of the initial node, so it should be graph.vertexs[1].connections
in your case.
In summary, to make the code work as expected, you can modify the function as follows:
def dijkstra(self, current, currentDistance, distances, visited, unvisited, end):
for neighbour, distance in distances.items():
if neighbour.name not in unvisited:
continue
newDistance = currentDistance + distance
if unvisited[neighbour.name] is None or unvisited[neighbour.name] > newDistance:
unvisited[neighbour.name] = newDistance
visited[current] = currentDistance
if current == end:
return visited
del unvisited[current]
if not unvisited:
return True
candidates = [node for node in unvisited.items() if node[1]]
current, currentDistance = sorted(candidates)[0]
self.dijkstra(current, currentDistance, graph.vertexs[current].connections, visited, unvisited, end)
return visited
And call it as follows:
print("Shortest possible path from node 1 to 0:")
start = 1
end = 0
result = graph.dijkstra(start, 0, graph.vertexs[start].connections, {}, {node: None for node in graphList}, end)
cost = result[end]
path = " ".join([str(arrInt) for arrInt in list(result.keys())])
print(path, "costing", cost)
By doing this, the output becomes
Shortest possible path from node 1 to 0: 1 6 2 0 costing 15
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.