Dijkstra和Prim算法的區別
1.先說說prim算法的思想:
眾所周知,prim算法是一個最小生成樹算法,它運用的是貪心原理(在這里不再證明),設置兩個點集合,一個集合為要求的生成樹的點集合A,另一個集合為未加入生成樹的點B,它的具體實現過程是:
第1步:所有的點都在集合B中,A集合為空。
第2步:任意以一個點為開始,把這個初始點加入集合A中,從集合B中減去這個點(代碼實現很簡單,也就是設置一個標示數組,為false表示這個點在B中,為true表示這個點在A中),尋找與它相鄰的點中路徑最短的點,如后把這個點也加入集合A中,從集合B中減去這個點(代碼實現同上)。
第3步:集合A中已經有了多個點,這時兩個集合A和B,只要找到A集合中的點到B集合中的點的最短邊,可以是A集合中的與B集合中的點的任意組合,把這條最短邊有兩個頂點,把在集合B中的頂點加入到集合A中,(代碼實現的時候有點技巧,不需要枚舉所有情況,也就是更新操作)。
第4步:重復上述過程。一直到所有的點都在A集合中結束。
2.說說dijkstra算法的過程:
這個算法的過程有比prim算法的過程稍微多一點點步驟,但是思想確實巧妙的,也是貪心原理,它的目的是求某個源點到目的點的最短距離,總的來說,dijkstra算法也就是求某個源點到目的點的最短路,求解的過程也就是求源點到整個圖的最短距離,次短距離,第三短距離等等(這些距離都是源點到某個點的最短距離)。。。。。求出來的每個距離都對應着一個點,也就是到這個點的最短距離,求的也就是原點到所有點的最短距離,並且存在了一個二維數組中,最后給出目的點就能直接通過查表獲得最短距離。
第1步:以源點(假設是s1)為開始點,求最短距離,如何求呢? 與這個源點相鄰的點與源點的距離全部放在一個數組dist[]中,如果不可達,dist[]中為最大值,這里說一下,為什么要是一維數組,原因是默認的是從源點到這個一維數組下標的值,只需要目的點作為下標就可以,這時從源點到其他點的最短的“一”條路徑有了,只要選出dist[]中最小的就行(得到最短路徑的另一個端點假設是s2)。
第2步:這時要尋找源點(假設是s1)到另外點的次短距離,這個距離或者是dist[]里面的值,或者是從第1步中選擇的那個最短距離 + 從找到點(假設是s2)出發到其他點的距離(其實這里也是一個更新操作,更新的是dist[]里面的值),如果最短距離 + 從這點(假設是s2)到其他點的距離,小於dist[]里面的值,就可以更新dist[]數組了,然后再從dist[]數組中選一個值最小的,也就是第“二”短路徑(次短路徑)。
第3步:尋找第“三”短路徑,這時同上,第二短路徑的端點(s3)更新與之相鄰其他的點的dist[]數組里面的值。
第4步:重復上述過程n - 1次(n指的是節點個數),得出結果,其實把源點到所有點的最短路徑求出來了,都填在了dist[]表中,要找源點到哪個點的最短路,就只需要查表了。
Dijkstra模版:

//dijkstra算法模版,狄傑斯特拉算法模板,單源最短路模板 //dijkstra算法模版 #include <stdio.h> #include <string.h> #define MaxN 1001 #define MaxInt 200000000 int map[MaxN][MaxN],dist[MaxN]; bool mark[MaxN]; int n,start,end; int main() { int i,j,min1,minj,temp; //****************輸入********************** scanf("%d%d%d",&n,&start,&end); for (i=1;i<=n;i++) for (j=1;j<=n;j++) scanf("%d",&map[i][j]); //****************************************** //*****************初始化********************** for (i=1;i<=MaxN;i++) dist[i]=MaxInt; memset(mark,0,sizeof(mark)); dist[start]=0; //把起點並入集合,搜索時就可以從起點尋找到第一條最短的邊了 //********************************************* for (i=1;i<=n-1;i++) { min1=MaxInt; for (j=1;j<=n;j++) //查找到原集合的最短的邊 { if (!mark[j]&&dist[j]<min1) { min1=dist[j]; minj=j; } } mark[minj]=1; for (j=1;j<=n;j++) //每並入一個點都要對原來的邊進行修正,保證任意時刻源點到目標點的距離都是最短的。 { if (!mark[j]&&map[minj][j]>0) { temp=dist[minj]+map[minj][j]; if (temp<dist[j]) dist[j]=temp; } } } //***********輸出*************** printf("%d\n",dist[end]); //****************************** return 0; }
Prim算法中尋找的是下一個與MST中任意頂點相距最近的頂點;
Dijkstra算法尋找的是下一個離給定頂點(單源)最近的頂點。
另外,當有兩條具有同樣的最小權值的邊可供選擇時,任選一條即可,所以構造的MST不是惟一的。
Prim算法和Dijkstra算法十分相似,惟一的區別是: Prim算法要尋找的是離已加入頂點距離最近的頂點;
Dijkstra算法是尋找離固定頂點距離最近的頂點。
所以Prim算法的時間復雜度分析與Dijkstra算法相同,都是 O(|V^2|)
【拓】:Kruskal算法:http://baike.baidu.com/link?url=MchMLaw4a3nLu3bWSoEUEak-DYbM8n0H27ANKE5-Gv_frudxAvGfsqdpNRqDtdB0
克魯斯卡爾(Kruskal)算法(只與邊相關)
算法描述:克魯斯卡爾算法需要對圖的邊進行訪問,所以克魯斯卡爾算法的時間復雜度只和邊又關系,可以證明其時間復雜度為O(eloge)。
算法過程:
1.將圖各邊按照權值進行排序
2.將圖遍歷一次,找出權值最小的邊,(條件:此次找出的邊不能和已加入最小生成樹集合的邊構成環),若符合條件,則加入最小生成樹的集合中。
不符合條件則繼續遍歷圖,尋找下一個最小權值的邊。
3.遞歸重復步驟1,直到找出n-1條邊為止(設圖有n個結點,則最小生成樹的邊數應為n-1條),算法結束。得到的就是此圖的最小生成樹。
克魯斯卡爾(Kruskal)算法因為只與邊相關,則適合求稀疏圖的最小生成樹。而prime算法因為只與頂點有關,所以適合求稠密圖的最小生成樹。
代碼:

#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<algorithm> using namespace std; #define MAX 1000 int father[MAX], son[MAX]; int v, l; typedef struct Kruskal //存儲邊的信息 { int a; int b; int value; }; bool cmp(const Kruskal & a, const Kruskal & b) { return a.value < b.value; } int unionsearch(int x) //查找根結點+路徑壓縮 { return x == father[x] ? x : unionsearch(father[x]); } bool join(int x, int y) //合並 { int root1, root2; root1 = unionsearch(x); root2 = unionsearch(y); if(root1 == root2) //為環 return false; else if(son[root1] >= son[root2]) { father[root2] = root1; son[root1] += son[root2]; } else { father[root1] = root2; son[root2] += son[root1]; } return true; } int main() { int ncase, ltotal, sum, flag; Kruskal edge[MAX]; scanf("%d", &ncase); while(ncase--) { scanf("%d%d", &v, &l); ltotal = 0, sum = 0, flag = 0; for(int i = 1; i <= v; ++i) //初始化 { father[i] = i; son[i] = 1; } for(int i = 1; i <= l ; ++i) { scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].value); } sort(edge + 1, edge + 1 + l, cmp); //按權值由小到大排序 for(int i = 1; i <= l; ++i) { if(join(edge[i].a, edge[i].b)) { ltotal++; //邊數加1 sum += edge[i].value; //記錄權值之和 cout<<edge[i].a<<"->"<<edge[i].b<<endl; } if(ltotal == v - 1) //最小生成樹條件:邊數=頂點數-1 { flag = 1; break; } } if(flag) printf("%d\n", sum); else printf("data error.\n"); } return 0; }