Chapter 42: Dijkstra’s Algorithm
前言
Dijkstra’s algorithm is particularly useful in GPS networks to help find the shortest path between two places.
Dijkstra’s algorithm is a greedy algorithm.
A greedy algorithm constructs a solution step-by-step, and it picks the most optimal path at every step.
大綱
Example
Implementation
Helper methods
Tracing back to the start
Calculating total distance
Generating the shortest paths
Finding a specific path
Trying out your code
Performance
Example
Every value in the output table has two parts:
the total cost to reach that vertex
the last neighbor on the path to that vertex.
For example, the value 4 G in the column for vertex C means that the cost to reach C is 4
Init
public enum Visit<T: Hashable> {
case start
// The vertex has an associated edge that leads to a path back to the starting vertex.
case edge(Edge<T>)
}
public class Dijkstra<T: Hashable> {
public typealias Graph = AdjacencyList<T>
let graph: Graph
public init(graph: Graph) {
self.graph = graph
}
}
Tracing back to the start
// Tracing back to the start
// 從目前所在的點回到起始點的路徑
// paths: 記錄在每個點時拜訪其他點的紀錄
private func route(to destination: Vertex<T>, with paths: [Vertex<T> : Visit<T>]) -> [Edge<T>] {
var vertex = destination
var path: [Edge<T>] = []
// As long as you have not reached the start case, continue to extract the next edge
while let visit = paths[vertex], case .edge(let edge) = visit {
path = [edge] + path
// Set the current vertex to the edge’s source vertex. This moves you closer to the start vertex.
vertex = edge.source
}
return path
}
Calculating total distance
// Calculating total distance
private func distance(to destination: Vertex<T>, with paths: [Vertex<T> : Visit<T>]) -> Double {
let path = route(to: destination, with: paths)
let distance = path.compactMap { $0.weight }
return distance.reduce(0.0, +)
}
Generating the shortest paths
public func shortestPath(from start: Vertex<T>) -> [Vertex<T> : Visit<T>] {
var paths: [Vertex<T> : Visit<T>] = [start : .start]
// Create a min-priority queue to store the vertices that must be visited.
var priorityQueue = PriorityQueue<Vertex<T>> (sort:{
// distance越小越優先
self.distance(to: $0, with: paths) < self.distance(to: $1, with: paths)
})
// Enqueue the start vertex as the first vertex to visit
priorityQueue.enqueue(start)
while let vertex = priorityQueue.dequeue() {
// 找到與此點相連所有邊
for edge in graph.edges(from: vertex) {
guard let weight = edge.weight else { continue }
// 此邊的另一端點尚未拜訪過
// 或找到一條更cheaper path
if paths[edge.destination] == nil || distance(to: vertex, with: paths) + weight < distance(to: edge.destination, with: paths) {
paths[edge.destination] = .edge(edge)
priorityQueue.enqueue(edge.destination)
}
}
}
return paths
}
Finding a specific path
public func shortestPath(to destination: Vertex<T>,
paths: [Vertex<T> : Visit<T>]) -> [Edge<T>] {
return route(to: destination, with: paths)
}
Performance
Constructed your graph using an adjacency list
Used a min-priority queue to store vertices and extract the vertex with the minimum path.
heap operations of extracting the minimum element or inserting an element both take O(log V)
Dijkstra’s algorithm is somewhat similar to breadth-first search
O(V + E) to traverse all the vertices and edges
use a min-priority queue to select a single vertex with the shortest distance to traverse down. That means it is O(1 + E) or simply O(E)
it takes O(E log V) to perform Dijkstra’s algorithm
Last updated
Was this helpful?