圖的算法專題——最小生成樹
阿新 • • 發佈:2019-02-23
流程 class 最短 端點 優化 等於 d+ size truct
概要:
- Prim算法
- Kruskal算法
1、Prim算法
算法流程:
(1)對圖G(V,E)設置集合S來存放已被並入的頂點,然後執行n次(2)(3)
(2)每次從未並入頂點集合中選擇與集合S最近的一個頂點,訪問u並將其加入集合S,同時把這條離集合S最近的邊加入最小生成樹中。
(3)優化松弛
Prim算法與Dijkstra算法使用的思想幾乎完全相同,只有在數組d[]的含義上有區別。(關於Dijkstra算法具體可以看前面寫的《圖的算法專題——最短路徑》)
Dijkstra中d[]含義為起點s到達頂點Vi的最短距離,而Prim算法d[]含義為頂點Vi與集合S的最短距離,兩者區別僅在於最短距離是到 起點 還是 集合 。
鄰接矩陣版:
1 const int maxn=1000;
2 const int INF=1000000000;
3 int n,G[maxn][maxn];
4 int d[maxm]; //頂點到集合S的距離
5 bool vis[maxn]={false};
6
7 int prim(){ //默認0號為初始點,函數返回最小生成樹的邊權之和
8 fill(d,d+maxn,INF);
9 d[0]=0;
10 int ans=0; //邊權之和
11 for(int i=0;i<n;i++){
12 int u=-1,MIN=INF;
13 for (int j=0;j<n;j++){
14 if(vis[j]==false&&d[j]<MIN){
15 u=j;
16 MIN=d[j];
17 }
18 }
19 if(u==-1) return -1;
20 vis[u]=true;
21 ans+=d[u]; //將與集合S距離最小的邊加入最小生成樹
22 for(int v=0;v<n;v++){
23 if (vis[v]==false && G[u][v]!=INF && G[u][v]<d[v] ){
24 d[v]=G[u][v]; //以u為中介點可以使v離集合S更近(u已被並入集合)
25 }
26 }
27 }
28 return ans;
29 }
鄰接表版:
1 struct Node{
2 int v,dis; //v為目標頂點,dis為邊權
3 };
4 vector<Node> Adj[maxn];
5 int n,d[maxn];
6 bool vis[maxn]={false};
7 int prim(){
8 fill(d,d+maxn,INF);
9 d[0]=0;
10 int ans=0;
11 for(int i=0;i<n;i++){
12 int u=-1,MIN=INF;
13 for(int j=0;j<n;j++){
14 if(vis[j]==false && d[j]<MIN){
15 u=j;
16 MIN=d[j];
17 }
18 }
19 if(u==-1) return -1;
20 vis[u]=true;
21 ans+=d[u];
22 for(int j=0;j<Adj[u].size();j++){
23 int v=Adj[u][j].v;
24 if(vis[v]==false && Adj[u][j].dis< d[v]){
25 d[v]=Adj[u][j].dis;
26 }
27 }
28 }
29 return ans;
30 }
2、Kruskal算法
算法流程:
(1)對所有邊權從小到大排序。
(2)按邊權從小到大測試所有邊,如果當前測試邊所連接的兩個頂點不在同一個連通塊中,則並入,否則舍棄。 (運用並查集)
(3)循環執行(2)直到最小生成樹的邊數等於總頂點數減1或是測試完所有邊時結束。若結束時邊數不為頂點數減1,則圖不連通。
1 const int MAXV=110;
2 const int MAXE=10010;
3 int father[MAXV];
4 int findFather(int x){
5 int a=x;
6 while(x!=father[x]){
7 x=father[x];
8 }
9 while(a!=father[a]){
10 int z=a;
11 a=father[a];
12 father[z]=x;
13 }
14 return x;
15 }
16 struct edge{
17 int u,v; //邊的兩個端點編號
18 int cost; //邊權
19 }E[MAXE];
20 bool cmp(edge a,edge b){
21 return a.cost<b.cost;
22 }
23 int kruskal(int n,int m){ //n為頂點個數,m為圖的邊數
24 int ans=0,Num_Edge=0;
25 for(int i=1;i<=n;i++) father[i]=i; //假設頂點範圍為1到n
26 sort(E,E+m,cmp);
27 for(int i=0;i<m;i++){ //枚舉邊
28 int faU=findFather(E[i].u);
29 int faV=findFather(E[i].v);
30 if(faU!=faV){
31 father[faU]=faV;
32 ans+=E[i].cost;
33 Num_Edge++;
34 if(Num_Edge==n-1) break;
35 }
36 }
37 if(Num_Edge != n-1) return -1;
38 else return ans;
39 }
圖的算法專題——最小生成樹