普里姆算法 Prim

原先一直没注意这个算法,一直以为迪杰斯特拉可以解决相关问题。后来发现迪杰斯特拉必须要有起点,否则就gg了。而这也是迪杰斯特拉和普里姆的最大区别。

算法简述

普里姆算法的本质是贪心。

总体上将所有节点分成两类:已确定从起点到该节点最短路长度的节点以及未确定从起点到该节点最短路长度的节点。
每次从尚未确定距离的节点中找到距离起点最近的点作为新的确定最短路长度的节点,并用它更新从起点到其他所有尚未确定距离节点的距离,即比较路径长短(也有书把这种更新的操作叫作松弛),直到所有点都被确定长度。

常见疑惑的说明:

1.是否可能需要再次更新? 由于已经使用每一个确定最短路长度的节点更新过当前节点,故无需再次更新(因为一个点不能多次到达)。 2.为何把距离起点最近的点作为新的确定最短路长度的节点? 由于该节点是所有尚未确定距离节点中距离起点最短的点,不可能被其它尚未确定距离的节点更新。所以该节点可以作为新的确定最短路长度的节点。

代码实现

class Prim{
public:
    int prim(int n, vector<vector<int>>& edges) {
        vector<vector<pair<int,int>>> routes(n+1);
        int ans=0,cnt=0;
        for(const auto& edge:edges)
        {
            routes[edge[0]].push_back(make_pair(edge[1],edge[2]));
            routes[edge[1]].push_back(make_pair(edge[0],edge[2]));
        }
        vector<int> used(n+1);
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> pq;//pq stores {distance,target}

        pq.emplace(0,1);//choose a starting point
        while(!pq.empty()&&cnt<n)
        {
            auto node=pq.top();
            pq.pop();
            int dis=node.first,x=node.second; 
            if(!used[x])
            {
                ans+=dis;
                used[x]=1;
                cnt++;
                for(const auto &route:routes[x])//traverse the edge starting from vertex x
                    pq.emplace(route.second,route.first);
            } 
        }

        if(cnt<n)
            return -1;
        return ans;
    }
};
时间复杂度 空间复杂度 补充说明 适用范围
采用堆 \(O(n+eloge)\) \(O(n+e)\) 无 顶点数大于边数(稀疏图)

e为边数,n为edges长度

例题:
力扣743网络延迟时间 https://leetcode-cn.com/problems/network-delay-time/