Chapter 44: Prim’s Algorithm

前言

  • Prim’s algorithm, a greedy algorithm used to construct a minimum spanning tree

    • 什麼是spanning tree

      • A spanning tree is a subgraph of an undirected graph, containing all of the graph’s vertices, connected with the fewest number of edge

      • cannot contain a cycle and cannot be disconnected

大綱

  • Example

  • Implementation

    • Helper methods

      • Copying a graph

      • Finding edges

    • Producing a minimum spanning tree

  • Testing your code

  • Performance

Example

Copying a graph

  • Create a minimum spanning tree, you must include all vertices from the original graph.

    // 複製圖上的所有點
    public func copyVertices(from graph: AdjacencyList) {
        for vertex in graph.vertices {
            adjacencies[vertex] = []
        }
    }

Finding edges

  • Find and store the edges of every vertex you explore.

    internal func addAvailableEdges(for vertex: Vertex<T>,
                           in graph: Graph,
                           check visited: Set<Vertex<T>>,
                           to priorityQueue: inout PriorityQueue<Edge<T>>) {
        // 對於目前的點所有相連的邊
        for edge in graph.edges(from: vertex) {
            // 若對應的想連的點,之前並未被拜訪過
            if !visited.contains(edge.destination) {
                // 將此邊加入priorityQueue中
                priorityQueue.enqueue(edge)

            }
        }
    }

Producing a minimum spanning tree

    public func produceMinimumSpanningTree(for graph: Graph) -> (cost: Double, mst: Graph) {
        var cost = 0.0
        let mst = Graph()
        var visited: Set<Vertex<T>> = []
        var priorityQueue = PriorityQueue<Edge<T>>(sort: {
            $0.weight ?? 0.0 < $1.weight ?? 0.0
        })
        // Copy all the vertices from the original graph to the minimum spanning tree
        mst.copyVertices(from: graph)

        guard let start = graph.vertices.first else {
            return (cost: cost, mst: mst)
        }

        visited.insert(start)
        // Add all potential edges from the start vertex into the priority queue.
        addAvailableEdges(for: start, in: graph, check: visited, to: &priorityQueue)

        while let smallestEdge = priorityQueue.dequeue() {
            let vertex = smallestEdge.destination
            // If this vertex has been visited, restart the loop and get the next smallest edge.”
            guard !visited.contains(vertex) else {
                continue
            }

            visited.insert(vertex)
            cost += smallestEdge.weight ?? 0.0

            // Add the smallest edge into the minimum spanning tree you are constructing.
            mst.add(.undirected, from: smallestEdge.source, to: smallestEdge.destination, weight: smallestEdge.weight)
            // Add the available edges from the current vertex
            addAvailableEdges(for: vertex, in: graph, check: visited, to: &priorityQueue)
        }

        return (cost: cost, mst: mst)
    }

Performance

  • Adding vertices and edges to an adjacency list is O(1)

  • Checking if the set contains a vertex also have a time complexity of O(1)

  • The priority queue is built on top of a heap and insertion takes O(log E).

  • The worst-case time complexity of Prim’s algorithm is O(E log E).

    • This is because, each time you dequeue the smallest edge from the priority queue, you have to traverse all the edges of the destination vertex ( O(E) ) and insert the edge into the priority queue ( O(logE) )

Last updated

Was this helpful?