標籤:article prim 父節點 span *** int algo bsp type
Kruskal演算法
Kruskal演算法
求解最小產生樹的還有一種常見演算法是Kruskal演算法。它比Prim演算法更直觀。從直觀上看,Kruskal演算法的做法是:每次都從剩餘邊中選取權值最小的,當然,這條邊不能使已有的邊產生迴路。
手動求解會發現Kruskal演算法異常簡單,以下是一個範例
先對邊的權值排個序:1(0,1) 2(2,6) 4(1,3) 6(1,2) 8(3,6) 10(5,6) 12(3,5) 15(4,5) 20(0,1)
首選邊1(0,1)、2(2,6)、4(1,3)、6(1,2),此時的圖是這樣
顯然,若選取邊8(3,6)會出現環,則必須拋棄8(3,6),選擇下一條10(5,6)沒有問題。此時圖變成這樣
顯然,12(3,5)相同不可取,選取15(4,5),邊數已達到要求,演算法結束。終於的圖是這種
演算法邏輯人非常easy理解。但用代碼推斷當前邊是否會引起環的出現。則非常棘手。
演算法說明
為了推斷環的出現,我們換個角度來理解Kruskal演算法的做法:初始時,把圖中的n個頂點看成是獨立的n個連通分量,從樹的角度看,也是n個根節點。我們選邊的標準是這種:若邊上的兩個頂點從屬於兩個不同的連通分量。則此邊可取,否則考察下一條權值最小的邊。
於是問題又來了,怎樣推斷兩個頂點是否屬於同一個連通分量呢?這個能夠參照並查集的做法解決。它的思路是:假設兩個頂點的根節點是一樣的,則顯然是屬於同一個連通分量。
這也相同暗示著:在增加新邊時,要更新父節點。
詳細細節看代碼:
代碼
#include<iostream> #include<algorithm>using namespace std;#define MAXWEIGHT 100/*全域變數numV頂點數numE邊數*/int numV, numE;//邊typedef struct edge_tag{int tail;int head;int weight;}Edge;//檢測邊bool checkEdge(int tail, int head, int weight){if (tail == head|| tail < 0 || tail >= numV|| head < 0 || head >= numV|| weight <= 0 || weight >= MAXWEIGHT)return false;return true;}//輸入邊void inputEdge(Edge *edge, int numE){int i, tail, head, weight;cout << "輸入邊的起點、終點和權值" << endl;i = 0;while (i < numE){cin >> tail >> head >> weight;while (!checkEdge(tail, head, weight)){cout << "輸入錯誤!又一次輸入" << endl;cin >> tail >> head >> weight;}edge[i].tail = tail;edge[i].head = head;edge[i].weight = weight;i++;}}int cmp(const void *edge1, const void *edge2){return ((Edge*)edge1)->weight - ((Edge*)edge2)->weight;}//並查集的常見操作/*壓縮尋找尋找child的根節點*/int Find(int child, int *parent){if (parent[child] == child)return child;parent[child] = Find(parent[child], parent); //壓縮路徑return parent[child];}//合并bool Union(Edge *e, int *parent, int *childs){//處於同一個棵樹中。則不能合并。否則會出現環int root1, root2;root1 = Find(e->tail, parent);root2 = Find(e->head, parent);if (root1 != root2){//把小樹合并到大樹根下if (childs[root1] > childs[root2]){parent[root2] = root1;childs[root1] += childs[root2] + 1;}else{parent[root1] = root2;childs[root2] += childs[root2] + 1;}return true;}return false;}/*Kruskal演算法求最小產生樹*/void Kruskal(int numV, int numE){//邊的集合Edge *edge = new Edge[numE];inputEdge(edge, numE);/*parent[i]是頂點i的父頂點childs[i]是頂點i的孩子數child的複數是children,這裡的childs是杜撰的*/int *parent = new int[numV];int *childs = new int[numV];//初始化for (int i = 0; i < numV; i++){/*初始時。每個頂點都是根。且無孩子把每個頂點的的父節點設定為自身下標,也是標明類別*/parent[i] = i;childs[i] = 0;}//對邊按權值進行從小到大排序qsort(edge, numE, sizeof(Edge), cmp);int i, count;i = count = 0;cout << "最小產生樹的邊..." << endl;while (i < numE){if (Union(&edge[i], parent, childs)){cout << edge[i].tail << "---" << edge[i].head << endl;count++;}if (count == numV - 1) //邊數達到要求break;i++;}if (count != numV - 1)cout << "此圖為非連通圖!無法構成最小產生樹。" << endl;delete[]edge;delete[]parent;delete[]childs;}//檢測頂點數和邊數bool checkVE(int numV, int numE){if (numV <= 0 || numE <= 0 || numE > numV*(numV - 1) / 2)return false;return true;}int main(){cout << "******Kruskal***by David***" << endl;cout << "輸入頂點數、邊數 ";cin >> numV >> numE;while (!checkVE(numV, numE)){cout << "輸入資料有問題!又一次輸入 ";cin >> numV >> numE;}cout << endl << "Kruskal..." << endl;Kruskal(numV, numE);system("pause");return 0;}
執行
轉載請註明出處,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/38414683
若有所協助,頂一個哦。
專欄檔案夾:
資料結構:最小產生樹--Kruskal演算法