數據結構圖之二(最小生成樹--普里姆算法)


【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 }
View Code

代碼中所引用是頭文件SeqList.h從隨筆《順序表》拷貝即可,也可以自己另行處理。

 

Good  Good  Study,  Day  Day  Up.

順序   選擇   循環   總結 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM