【算法】prim算法(最小生成树)(与Dijkstra算法的比较)
最小生成樹:
生成樹的定義:給定一個無向圖,如果它的某個子圖中任意兩個頂點都互相連通并且是一棵樹,那么這棵樹就叫做生成樹。(Spanning Tree)
最小生成樹的定義:在生成樹的基礎上,如果邊上有權值,那么使得邊權和最小的生成樹叫做最小生成樹。(Minimum Spanning Tree )
解決生成樹有兩種常用的算法:Kruskal算法和prim算法。
這里我們講的是prim算法求生成樹的解法。
算法思想:
ans = 0;(表示權值和)
1.在無向圖的基礎上,想象我們有一個點的集合X(初始狀態為空)。
2.在集合X中加入一個初始點x,用這個初始點更新其他點離集合X的距離mincost[ ],標記初始點為使用過(使用過:加入集合X)。
3.找一個未使用過(集合X外的點)的離集合X最小距離最小的點,找到了這樣一個點,將這個點加入集合X,ans += 這個與集合X的距離,
用這個新加入的點更新其他點離集合X的最小距離mincost[ ],標記新加入的點為使用過,繼續執行第3步;找不到這樣一個點,則進入第4步。
4.輸出ans。
mincost[i] 表示點i離集合X的最小距離。(離集合X中所有點中最近的點的距離)
簡單來說就是:
想象一下,有10張面額不同的毛爺爺在你面前,每次只能拿1張,只能拿5次,你肯定會每次都拿這n張(n<=10)中最大的那張,
這樣拿5次,讓總額最大。這個算法也是同樣的道理,不斷的加入可觸及的最近的點,最后權值一定是最小的。
額,當然10張不同的毛爺爺是不存在的。所以一分都拿不到,還是看代碼吧:
代碼:
#include <bits\stdc++.h> using namespace std; #define INF 2147483647 #define MAX_V 1000 #define MAX_E 2000 int cost[MAX_V][MAX_V]; // cost[i][j] 表示頂點i到頂點j的權值,不存在時為INF int mincost[MAX_V]; // mincost[i] 表示i點與集合X的最小距離 bool used[MAX_V]; // used[i]表示點i是否在集合中 int V; //頂點數 //表示從點x產生的最小生成樹,這么考慮是因為整個圖可能不連通 int prim(int x){//最初X集合為空,每個點到集合X的最小距離都是INF for(int i = 0;i < V; ++i){mincost[i] = INF;used[i] = false;}//將點x與集合X的距離置為0,第一次集合X會加入點x mincost[x] = 0;int res = 0;while(true){int v = -1;//找到離集合X最近的點,第一次加入點x for(int u = 0;u < V; ++u){if(!used[u] && (v == -1 || mincost[u] < mincost[v])) v = u;}//如果所有點可達的點都加入集合X中了,就跳出 if(v == -1) break;used[v] = true;res += mincost[v];mincost[v] = 0; //把點v加入到集合X中,這一步幫助理解,可寫可不寫 ,因為cost[v][v] = 0 //用新加入的點v更新其他點離與集合X的最小距離 for(int u = 0;u < V; ++u){mincost[u] = min(mincost[u] , cost[v][u]);}}return res; }int main(){ }與Dijkstra算法的比較
Prim算法與Dijkstra算法都是從某個點出發,不斷加入最近的點。最終都要把所有可以加的點加完。
Prim算法是求最小生成樹,Dijkstra是求單源最短路徑。
來個Dijkstra求單源最短路徑的代碼,與Prim算法比較一下:
#include <bits\stdc++.h> using namespace std; #define INF 2147483647 #define MAX_V 1000 #define MAX_E 2000 //單源最短路徑問題(Dijkstra算法) int cost[MAX_V][MAX_V]; //cost[u][v]表示e = (u,v)的權值 int d[MAX_V]; //頂點s出發的最短距離 //不同處1 bool used[MAX_V]; //標記使用過的點 int V; //頂點數 void dijkstra(int s){fill(d, d+V, INF);fill(used, used + V, INF);d[s] = 0;while(true){int v = -1;//找到一個距離最近的沒有使用過的點 for(int u = 0;u < V; u++){if(!used[u] && (v == -1 || d[u] < d[v])) v = u;}//如果所有的點都被使用過了,則breakif(v == -1) break;//標記當前點被使用過了 used[v] = true;//更新這個找到的距離最小的點所連的點的距離 for(int u = 0;u < V; u++){d[u] = min(d[u], d[v] + cost[v][u]); //不同處2}} }int main(){ }我們可以看到代碼基本上是一樣的,只有
不同處1:Djikstra中用d[i]表示i點離源點的最短距離,Prim中用mincost[i] 表示i點與集合X的距離。
不同處2:Djikstra中更新d[u] = min( d[u] , d[v] + cost[v][u] ); 用新加入的點更新其他點與源點的最小距離。
Prim中更新mincost[u] = min( mincost[u] , cost[v][u] ); 用新加入的點更新點i與集合X的最小距離。
我認為只用加幾行代碼,無論是在prim中加,還是在Dijkstra中加,就既可以求單源最短路徑,又可以求最小生成樹了。
總結
以上是生活随笔為你收集整理的【算法】prim算法(最小生成树)(与Dijkstra算法的比较)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【设计模式】C++单例模式
- 下一篇: LeetCode4. Median of