最小生成樹-普林算法(Prim)/克魯斯卡爾算法(Kruskal)

問題提出:

        一個公司計劃建立一個通信網絡來連接它的一個計算機中心。可以用租用的電話線連接這些中心的任何一對。應當妊娠瘙癢哪些連接,以便保證在任何兩個計算機中心之間都有通路,且網絡的總成本最小?可以用下較長所示的帶權圖爲這個問題建模,其中頂點表示計算機中心,邊表示可能租用的電話線,邊上的權是邊所表示的電話線的月租費。通過找出一棵生成樹,使得這棵樹的各邊的權之和爲最小,就可以解決這個問題。這樣的生成樹稱爲最小生成樹。

最小生成樹定義:
    在一個具有V個節點的連通無向圖中,找到一個子圖,該子圖包含原圖的所有節點和部分連接邊,且不能形成迴路,同時子圖邊的權值總和最小。
最小生成樹的算法:
根據對安全邊的不同規則,有兩種算法可以生成最小生成樹。即Kruskal算法和Prim算法。

1.普林算法(Prim)

        該算法所具有的一個性質是集合A中的邊總是構成一棵樹,而不是森林。這棵樹從任意一個根節點開始,一直長大到覆蓋V中所有的節點爲止。算法每一步在鏈接集合A和A之外的節點的邊,選擇一條輕量級邊加入到集合A中。
步驟:
設是帶權值的連通圖,A是上最小生成樹中邊的集合:
(1)初始令,(), A=NULL
(2)在所有,的邊中,找一條權值最小的邊
(3)將併入集合A,同時併入
(4)重複上述操作直至爲止,則爲的最小生成樹
判斷規則:在所有,的邊中,找一條權值最小的邊,連接該邊,但不能形成迴路;


舉例:

該圖爲原始圖
第一步:選取一個初始節點a爲根節點,並找到權值爲最小的邊,即爲ab;


第二步:在所有,的邊中,找一條權值最小的邊;即權值最小邊爲bc或者ah;這裏選取bc


第三步:在所有,的邊中,找一條權值最小的邊;即選擇ci;


第四步:在所有,的邊中,找一條權值最小的邊;即選擇cf;


第五步:在所有,的邊中,找一條權值最小的邊;即選擇fg;


第六步:在所有,的邊中,找一條權值最小的邊;即選擇gh;


第七步:在所有,的邊中,找一條權值最小的邊;必須保證不能形成迴路,即選擇cd;




最後一步:在所有,的邊中,找一條權值最小的邊;必須保證不能形成迴路,即選擇de;由於樹包含了所有節點,且滿足,則在此終止,爲的最小生成樹。

         很簡單的地說,就是建立一棵樹T,和不在樹中的頂點集合L,找出兩者之間最短的距離的點,加入到樹中,直到所有點加進去爲止。
//d爲圖的鄰近矩陣,p爲所生成樹的鄰近矩陣,
//n爲圖中點的個數
void Prim(int **d,int **p,int n)
{
	int i,j,tmp,iT,iL;
	vector<int> T,L;//T是要生成的樹,L爲還沒生成樹的頂點
	for(i=0;i<n;i++)
		L.push_back(i);//給L賦初始
	while(L.size()){//當L爲空時,即已經生成樹完畢
		tmp=d[0][0];iT=iL=0;//以第一個點作爲初始點
		for(i=0;i<T.size();i++){//找出樹與非樹頂點之間距離最小的點,並記錄
			for(j=L.size()-1;j>=0;j--){
				if(tmp>=d[T[i]][L[j]]){
					tmp=d[T[i]][L[j]];
					iT=i;iL=j;
				}
			}
		}
		cout<<L[iL]<<" ";
		T.push_back(L[iL]);//將找到的點添加到樹中
		p[T[iT]][L[iL]]=1;
		L.erase(L.begin()+iL);//且從剩餘的點中刪除
	}
	cout<<endl;
}


時間複雜度:
最小邊、權的數據結構 時間複雜度(總計)
鄰接矩陣、搜索 O(V2)
二叉堆鄰接表 O((V + E) log(V)) = O(E log(V))
斐波那契堆鄰接表 O(E + V log(V))

2.克魯斯卡爾算法(Kruskal)



3.網絡資源

http://blog.csdn.net/chenhanzhun/article/details/39006205
http://blog.csdn.net/niushuai666/article/details/6689285
http://baike.baidu.com/view/247951.htm?fr=aladdin

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章