liveBook · Manning

Dijkstra's algorithm

As explained in Classic Computer Science Problems in Swift: Essential techniques for practicing programmers

Dijkstra's algorithm computes shortest paths from a source vertex to reachable vertices in a weighted graph with nonnegative edge weights.

Dijkstra's algorithm finds the lowest-weight paths from one starting vertex to every reachable vertex in a weighted graph. It repeatedly explores the closest known vertex, updates shorter routes, and records the edge that produced each distance. The technique underlies route planning and navigation systems. section 2.4 section 4.4

The algorithm proceeds as follows:

  • Add the starting vertex to an ascending priority queue. section 4.4
  • Pop the closest vertex and inspect every neighboring edge. section 4.4
  • Record a neighbor's distance and incoming edge when no route exists or the new route is shorter. section 4.4
  • Repeat until the queue is empty, then return all distances and predecessor edges. section 4.4

A Swift implementation stores optional distances in an array, predecessor edges in a dictionary, and queued vertices in a comparable DijkstraNode. For each edge, it replaces the old distance only when the accumulated weight is smaller. section 4.4

From Los Angeles to Boston

A city graph starting at Los Angeles reports Boston at distance 2605. The recorded path runs through Riverside, Chicago, and Detroit, with edge weights 50, 1704, 238, and 613.

public extension WeightedGraph {

    /// Represents a node in the priority queue used
    /// for selecting the next
    struct DijkstraNode: Comparable, Equatable {
        let vertex: Int
        let distance: W

        public static func < (lhs: DijkstraNode, rhs: DijkstraNode) -> Bool {
            return lhs.distance < rhs.distance
        }

        public static func == (lhs: DijkstraNode, rhs: DijkstraNode)
         -> Bool {
            return lhs.distance == rhs.distance
        }
    }

    /// Finds the shortest paths from some route vertex to every other
         vertex in the graph.
    ///
    /// - parameter graph: The WeightedGraph to look within.
    /// - parameter root: The index of the root node to build the shortest
         paths from.
    /// - parameter startDistance: The distance to get to the root node
         (typically 0).
    /// - returns: Returns a tuple of two things: the first, an array
         containing the distances, the second, a dictionary containing
         the edge to reach each vertex. Use the function
         pathDictToPath() to convert the dictionary into something
         useful for a specific point.
    public func dijkstra(root: Int, startDistance: W) -> ([W?],
     [Int: WeightedEdge<W>]) {
        var distances: [W?] = [W?](repeating: nil, count: vertexCount)
         // how far each vertex is from start
        distances[root] = startDistance // the start vertex is
         startDistance away
        var pq: PriorityQueue<DijkstraNode> =
         PriorityQueue<DijkstraNode>(ascending: true)
        var pathDict: [Int: WeightedEdge<W>] = [Int: WeightedEdge<W>]()
         // how we got to each vertex
        pq.push(DijkstraNode(vertex: root, distance: startDistance))

        while let u = pq.pop()?.vertex { // explore the next closest vertex
            guard let distU = distances[u] else { continue } // should
             already have seen it
            for we in edgesForIndex(u) { // look at every edge/vertex
             from the vertex in question
                let distV = distances[we.v] // the old distance to
                 this vertex
                if distV == nil || distV! > we.weight + distU { // if
                 we have no old distance or we found a shorter path
                    distances[we.v] = we.weight + distU
                     // update the distance to this vertex
                    pathDict[we.v] = we // update the edge on the shortest
                     path to this vertex
                    pq.push(DijkstraNode(vertex: we.v, distance:
                     we.weight + distU)) // explore it soon
                }
            }
        }

        return (distances, pathDict)
    }


    /// A convenience version of dijkstra() that allows the supply of
         the root
    /// vertex instead of the index of the root vertex.
    public func dijkstra(root: V, startDistance: W)
     -> ([W?], [Int: WeightedEdge<W>]) {
        if let u = indexOfVertex(root) {
            return dijkstra(root: u, startDistance: startDistance)
        }
        return ([], [:])
    }

    /// Helper function to get easier access to Dijkstra results.
    public func distanceArrayToVertexDict(distances: [W?]) -> [V : W?] {
        var distanceDict: [V: W?] = [V: W?]()
        for i in 0..<distances.count {
            distanceDict[vertexAtIndex(i)] = distances[i]
        }
        return distanceDict
    }
}

Inspect the Swift extension defining the priority-queue node and shortest-path operation. section 4.4

How Classic Computer Science Problems in Swift: Essential techniques for practicing programmers covers it

How deep it goes
Explains the five operating steps and Swift data flow, but does not provide a formal proof or asymptotic analysis.
Examples
Works through a city graph from Los Angeles to Boston and reconstructs the route through Riverside, Chicago, and Detroit.
What it assumes
Requires familiarity with weighted graphs, vertices, edges, priority queues, and basic Swift collections.
What you can do after
Build it from scratch by implementing distance relaxation, predecessor tracking, priority-queue exploration, and path reconstruction in Swift.
How it connects
Connects shortest paths with weighted graphs, priority queues, greedy algorithms, breadth-first search, and A*.

Also explained in

Compare how 5 books explain Dijkstra's algorithm →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Classic Computer Science Problems in Swift: Essential techniques for practicing programmers. The section links point to the parts of the book it draws on.