1. 程式人生 > >Prim 演算法及其高效實現

Prim 演算法及其高效實現

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

背景

最小生成樹(Minimum Spanning Trees),簡稱MST。是圖論中一個非常重要的概念。解決這個問題有兩種演算法,今天暫且先來討論一下Prim Algorithm。不做特別說明,討論的都是無向圖。

首先介紹一下最小生成樹的概念,我們知道,圖可以這樣定義 G=(V,E) ,其中 G 表示圖,V 表示頂點集合,E 表示邊集合。最小生成樹是這樣一棵樹,它滿足:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

通俗地講,就是使得圖GG連通時,所選取的邊的長度的和最小。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

如上圖,加粗的路徑就是在最小生成樹上的路徑。

演算法講解:

現在,我們開始討論Prim Algorithm。這個演算法可以分為下面幾個步驟:

將頂點集 V 分成兩個集合 A 和 B,其中集合 A 表示目前已經在MST中的頂點,而集合 B 則表示目前不在 MST 中的頂點。

尋找與集合 A 連通的最短的邊 (u,v),將這條邊加入最小生成樹中。(此時,與(u,v) 相連的頂點,不妨設為 Bi,也應加入集合 A 中)重複第二步,直至集合 B 為空集。

演算法的大體思想就是這樣了。為了方便理解,我們先來看一下下面一張圖片:

640?wx_fmt=png

對照上面的圖片,想必對於Prim Algorithm也有了一定的理解。

下面我們來設計演算法,顯然,我們需要遍歷集合 A 中所有頂點及與之相連的邊,取連線到集合B的權值最小的邊,加入最小生成樹。這樣一來,複雜度將達到 O(n3)。

我們可以對這個想法進行優化。我們維護一 pCost[i] 陣列,用來表示從集合A到與之相鄰的節點的最小費用。這樣,我們只要每次取這個陣列中的最小值,把它在集合B中所對應的結點Vi加入到集合A中。

每次加入結束以後,都要更新pCost[i]陣列。即列舉所有與結點Vi相連的邊,判斷是否比pCost[i]陣列中的最小費用小,如果比它小,則更新。這樣可以將演算法優化到O(n2)。

程式碼如下:

#include <iostream>

#include <memory.h> 

#include <vector>

using namespace std;

const int MAX = 1024;

const int INF = 2147483647;// 設定最大權值 

int N, M;

vector<pair<int, int> > pMap[MAX];// 鄰接表 

void Prim();

int main()

{

cin >> N >> M;

for(int i = 1; i <= M; i++)

{

int u, v, w;

cin >> u >> v >> w;

pMap[u].push_back(make_pair(v, w));

pMap[v].push_back(make_pair(u, w));

}

Prim();

return 0;

}

void Prim()

{

int nCost = 0;

vector<int> pMST;// 儲存MST的結點 

int pCost[MAX];// 儲存與集合A相鄰的頂點的最小權值,0表示該結點已經在MST中

pMST.push_back(1);// 將結點1加入MST

pCost[1] = 0;

for(int i = 2; i <= N; i++)// 初始化,切記要將除1以外的都置為INF

{ pCost[i] = INF; }

for(int i = 0; i < pMap[1].size(); i++)// 處理與結點1相連的頂點

{ pCost[pMap[1][i].first] = pMap[1][i].second; }

for(int i = 1; i <= N - 1; i++)// 剩餘N-1個頂點,迴圈N-1次

{

int nVertex = 0, nWeight = INF;// 用於尋找最短的邊

for(int j = 1; j <= N; j++)

{

if(nWeight > pCost[j] && pCost[j] != 0)

{

nVertex = j;

nWeight = pCost[j];

}

}

pCost[nVertex] = 0;

pMST.push_back(nVertex);// 將節點nVertex加入MST

nCost += nWeight;// 計算MST的費用

for(int j = 0; j < pMap[nVertex].size(); j++)// 更新pCost陣列

{

if(pCost[pMap[nVertex][j].first] != 0 && 

pCost[pMap[nVertex][j].first] > pMap[nVertex][j].second)

{

pCost[pMap[nVertex][j].first] = pMap[nVertex][j].second;

}

}

}

cout << "MST Cost is " << nCost << endl;

cout << "The vertexs in MST are ";

for(int i = 0; i < pMST.size(); i++)

{ cout << pMST[i] << " "; } 

cout << endl;

}

轉自:ivy-end

http://www.ivy-end.com/archives/943

640?wx_fmt=png