【1】什么是最小生成樹?
對於連通的帶權圖(連通網)G,其生成樹也是帶權的。
生成樹T各邊的權值總和稱為該樹的權。
權最小的生成樹稱為G的最小生成樹(Minimum SpannirngTree)。簡記為MST。
注意:最小是指權值最小
一個連通圖的生成樹是一個極小的連通子圖,它包含全部的頂點,但只有足以構成一棵樹的n-1條邊。
求最小生成樹有兩種算法:普里姆算法和克魯斯卡爾算法
不好理解?看不懂?能通俗點不?看個實例哈:
假設你是電信實施工程師,需要為一個鎮的九個村庄架設通信網絡做設計。
村庄位置大致如下圖,之間連線的數字表示村與村間的可通達直線距離。
你們領導要求你必須用最小的成本完成這次任務。你說怎么辦?
好,這就是很現實的一個最小生成樹案例。且聽下面詳解。
【2】普里姆算法
利用 普里姆算法 要解決如上問題,首先我們構造圖的鄰接矩陣。如下圖所示:
注意:實際中我們用65535來代表無窮大。
關於普里姆算法以及講解如下圖:
針對上面我們遇到的實際案例,普里姆算法執行循環過程如下圖:
每次所選最小邊分別如 圖1-圖8 所示:
最后用所有邊把各個頂點連通也就是所謂的最小生成樹。
【3】普里姆算法的實現
實現代碼如下:

1 #include <iostream> 2 #include "SeqList.h" 3 #include <iomanip> 4 using namespace std; 5 6 #define INFINITY 65535 7 8 template<class NameType, class DistType> 9 class Graph 10 { 11 private: 12 SeqList<NameType> Vertices; 13 DistType **Edges; 14 int nVer, nEdges; 15 16 public: 17 Graph() 18 : Edges(NULL) 19 , nEdges(0) 20 , nVer(0) 21 {} 22 ~Graph() 23 {} 24 25 public: 26 27 istream & operator>>(istream &in) 28 { 29 int v, u, value; 30 int i, j; 31 NameType item; 32 cout << "請輸入頂點的個數: " << endl; 33 in >> nVer; 34 cout << "請輸入頂點的數據信息: " << endl; 35 for (i = 0; i < nVer; ++i) 36 { 37 in >> item; 38 Vertices.push_back(item); // 保存全部頂點 39 } 40 /////二維數組的創建並初始化 41 Edges = new DistType*[nVer]; // DistType *ar[10]; 42 for (i = 0; i < nVer; ++i) 43 { 44 Edges[i] = new DistType[nVer]; 45 for (j = 0; j < nVer; ++j) 46 { 47 Edges[i][j] = 0; 48 } 49 } 50 cout << "請輸入邊的個數: " << endl; 51 in >> nEdges; 52 cout << "請輸入邊的信息:" << endl; 53 for (i = 0; i < nEdges; ++i) 54 { 55 in >> v >> u >> value; 56 Edges[v][u] = value; 57 Edges[u][v] = value; 58 } 59 return in; 60 } 61 ostream & operator<<(ostream &out) const 62 { 63 int i, j; 64 out << "頂點信息 " << endl; 65 for (i = 1; i <= nVer; ++i) 66 { 67 out << Vertices[i] << setw(5); 68 } 69 out << endl; 70 out << "矩陣信息:" << endl; 71 out << setw(10); 72 for (i = 1; i <= nVer; ++i) 73 { 74 out << Vertices[i] << setw(5); 75 } 76 out << endl; 77 for (i = 0; i < nVer; ++i) 78 { 79 out << Vertices[i+1] << setw(5); 80 for (j = 0; j < nVer; ++j) 81 { 82 if (0 == Edges[i][j] && i != j) 83 Edges[i][j] = INFINITY; 84 cout << Edges[i][j] << setw(5); 85 } 86 out << endl; 87 } 88 out << endl; 89 90 return out; 91 } 92 // 圖采用鄰接矩陣存儲 最小生成樹 普里姆算法 93 void MiniSpanTree() 94 { 95 int min = 0, i = 0, j = 0, k = 0; 96 int* adjvex = new int[nVer]; // 保存相關頂點下標 97 int* lowcost = new int[nVer]; // 保存相關頂點間邊的權值 98 lowcost[0] = 0; // 初始化第一個權值為0,即V0已加入生成樹 99 //lowcost的值為0,在這里就是此下標的頂點已經加入生成樹 100 adjvex[0] = 0; // 初始化第一個頂點下標為0 101 102 for (i = 1; i < nVer; ++i) //循環除過下標為0外的全部頂點 103 { 104 lowcost[i] = Edges[0][i]; // 將v0頂點與之有邊的權值存入數組 105 adjvex[i] = 0; // 並初始化都為v0的下標 106 } 107 108 for (i = 1; i < nVer; ++i) 109 { 110 min = INFINITY; // 初始化最小權值為常數,通常設置為不可能到達的數值 111 k = 0; // 復位 112 113 for (j = 1; j < nVer; ++j) 114 { 115 // 如果兩個頂點之間存在邊有權值,不為0並且小於min 116 if (lowcost[j] != 0 && lowcost[j] < min) 117 { 118 min = lowcost[j]; // 緩存最小值 119 k = j; // 將當前最小值的下標緩存入k 120 } 121 } 122 cout << "("<< adjvex[k] << "," << k << ")" << endl; //打印當前頂點邊中權值最小的邊 123 lowcost[k] = 0; // 將當前頂點的權值設為0,表示此頂點已經完成任務 124 125 for (j = 1; j < nVer; ++j) // 循環所有節點 126 { 127 if (lowcost[j] != 0 && Edges[k][j] < lowcost[j]) 128 { // 若下標為k頂點各邊權值小於此前這些頂點未被加入生成樹權值 129 lowcost[j] = Edges[k][j]; // 用較小者替換 130 adjvex[j] = k; // 將下標為k的頂點存入adjvex 131 } 132 } 133 } 134 135 delete []adjvex; 136 delete []lowcost; 137 adjvex = NULL; 138 lowcost = NULL; 139 } 140 }; 141 142 template<class NameType, class DistType> 143 istream & operator>>(istream &in, Graph<NameType,DistType> &g) 144 { 145 g >> in; 146 return in; 147 } 148 149 template<class NameType, class DistType> 150 ostream & operator<<(ostream &out, const Graph<NameType,DistType> &g) 151 { 152 g << out; 153 return out; 154 } 155 156 void main() 157 { 158 Graph<char, int> myg; 159 cin >> myg; 160 cout << "打印所有輸入信息:" << endl; 161 cout << myg << endl; 162 cout << "最小生成樹邊信息如下:" << endl; 163 myg.MiniSpanTree(); 164 cout << endl; 165 }
代碼中所引用是頭文件SeqList.h從隨筆《順序表》拷貝即可,也可以自己另行處理。
Good Good Study, Day Day Up.
順序 選擇 循環 總結