什麼是最小生成樹?html
最小生成樹(Minimum spanning tree,MST)是在一個給定的無向圖G(V,E)中求一棵樹T,使得這棵樹擁有圖G中的全部頂點,且全部邊都是來自圖G中的邊,而且知足整棵樹的邊權值和最小。ios
和Dijkstra算法很像!!請看以下Gif圖,prim算法的核心思想是對圖G(V,E)設置集合S,存放已被訪問的頂點,而後每次從集合V-S中選擇與集合S的最短距離最小的一個頂點(記爲u),訪問並加入集合S。以後,令頂點u爲中間點,優化全部從u能到達的頂點v與集合s之間的最短距離。這樣的操做執行n次,直到集合s中包含全部頂點。算法
不一樣的是,Dijkstra算法中的dist是從源點s到頂點w的最短路徑;而prim算法中的dist是從集合S到頂點w的最短路徑,如下是他們的僞碼描述對比,關於Dijkstra算法的詳細描述請參考文章ide
算法實現:測試
#include<iostream> #include<vector> #define INF 100000 #define MaxVertex 105 typedef int Vertex; int G[MaxVertex][MaxVertex]; int parent[MaxVertex]; // 並查集 int dist[MaxVertex]; // 距離 int Nv; // 結點 int Ne; // 邊 int sum; // 權重和 using namespace std; vector<Vertex> MST; // 最小生成樹 // 初始化圖信息 void build(){ Vertex v1,v2; int w; cin>>Nv>>Ne; for(int i=1;i<=Nv;i++){ for(int j=1;j<=Nv;j++) G[i][j] = 0; // 初始化圖 dist[i] = INF; // 初始化距離 parent[i] = -1; // 初始化並查集 } // 初始化點 for(int i=0;i<Ne;i++){ cin>>v1>>v2>>w; G[v1][v2] = w; G[v2][v1] = w; } } // Prim算法前的初始化 void IniPrim(Vertex s){ dist[s] = 0; MST.push_back(s); for(Vertex i =1;i<=Nv;i++) if(G[s][i]){ dist[i] = G[s][i]; parent[i] = s; } } // 查找未收錄中dist最小的點 Vertex FindMin(){ int min = INF; Vertex xb = -1; for(Vertex i=1;i<=Nv;i++) if(dist[i] && dist[i] < min){ min = dist[i]; xb = i; } return xb; } void output(){ cout<<"被收錄順序:"<<endl; for(Vertex i=1;i<=Nv;i++) cout<<MST[i]<<" "; cout<<"權重和爲:"<<sum<<endl; cout<<"該生成樹爲:"<<endl; for(Vertex i=1;i<=Nv;i++) cout<<parent[i]<<" "; } void Prim(Vertex s){ IniPrim(s); while(1){ Vertex v = FindMin(); if(v == -1) break; sum += dist[v]; dist[v] = 0; MST.push_back(v); for(Vertex w=1;w<=Nv;w++) if(G[v][w] && dist[w]) if(G[v][w] < dist[w]){ dist[w] = G[v][w]; parent[w] = v; } } } int main(){ build(); Prim(1); output(); return 0; }
關於prim算法的更加詳細講解請參考視頻優化
Kruskal算法也能夠用來解決最小生成樹的問題,其算法思想很容易理解,典型的邊貪心,其算法思想爲:ui
請看下面的Gif圖!spa
算法實現:code
#include<iostream> #include<string> #include<vector> #include<queue> #define INF 100000 #define MaxVertex 105 typedef int Vertex; int G[MaxVertex][MaxVertex]; int parent[MaxVertex]; // 並查集最小生成樹 int Nv; // 結點 int Ne; // 邊 int sum; // 權重和 using namespace std; struct Node{ Vertex v1; Vertex v2; int weight; // 權重 // 重載運算符成最大堆 bool operator < (const Node &a) const { return weight>a.weight; } }; vector<Node> MST; // 最小生成樹 priority_queue<Node> q; // 最小堆 // 初始化圖信息 void build(){ Vertex v1,v2; int w; cin>>Nv>>Ne; for(int i=1;i<=Nv;i++){ for(int j=1;j<=Nv;j++) G[i][j] = 0; // 初始化圖 parent[i] = -1; } // 初始化點 for(int i=0;i<Ne;i++){ cin>>v1>>v2>>w; struct Node tmpE; tmpE.v1 = v1; tmpE.v2 = v2; tmpE.weight = w; q.push(tmpE); } } // 路徑壓縮查找 int Find(int x){ if(parent[x] < 0) return x; else return parent[x] = Find(parent[x]); } // 按秩歸併 void Union(int x1,int x2){ if(parent[x1] < parent[x2]){ parent[x1] += parent[x2]; parent[x2] = x1; }else{ parent[x2] += parent[x1]; parent[x1] = x2; } } void Kruskal(){ // 最小生成樹的邊不到 Nv-1 條且還有邊 while(MST.size()!= Nv-1 && !q.empty()){ Node E = q.top(); // 從最小堆取出一條權重最小的邊 q.pop(); // 出隊這條邊 if(Find(E.v1) != Find(E.v2)){ // 檢測兩條邊是否在同一集合 sum += E.weight; Union(E.v1,E.v2); // 並起來 MST.push_back(E); } } } void output(){ cout<<"被收錄順序:"<<endl; for(Vertex i=0;i<Nv;i++) cout<<MST[i].weight<<" "; cout<<"權重和爲:"<<sum<<endl; for(Vertex i=1;i<=Nv;i++) cout<<parent[i]<<" "; cout<<endl; } int main(){ build(); Kruskal(); output(); return 0; }
關於kruskal算法更詳細的講解請參考視頻視頻