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