最小生成樹——普里姆算法(Prim算法)

普里姆算法:

普里姆算法在找最小生成樹時,將頂點分爲兩類,一類是在查找的過程中已經包含在樹中的(假設爲 A 類),剩下的是另一類(假設爲 B 類)。

對於給定的連通網,起始狀態全部頂點都歸爲 B 類。在找最小生成樹時,選定任意一個頂點作爲起始點,並將之從 B 類移至 A 類;然後找出 B 類中到 A 類中的頂點之間權值最小的頂點,將之從 B 類移至 A 類,如此重複,直到 B 類中沒有頂點爲止。所走過的頂點和邊就是該連通圖的最小生成樹。
關於普里姆算法詳細介紹可以看以下視頻:
普里姆算法非常詳細

算法思路

以下無向圖爲例:
在這裏插入圖片描述

1. 建立鄰接矩陣

首先建立二維鄰接矩陣arcs,兩點之間如果連通則arcs[i] [j] = 權值,如果不連通則設置爲一個很大的值自己設定以下用M表示。建立的鄰接矩陣如下:

在這裏插入圖片描述
2.建立輔助數組
建立一個結構體數組,結構體中包含權值最小邊的起始點和其對應的最小權值,這個數組在每一次找邊的時候都會用到,因此需要設爲全局變量。
closedge theclose 具體用法後面介紹

typedef struct {
	VertexType adjvex;//記錄權值最小的邊的起始點
	VRType lowcost;//記錄該邊的權值
}closedge[MAX_VERtEX_NUM]

3.開始找邊

我們一上圖的A爲第一個點,開始。
首先將點A到各個點的權值存儲在輔助數組theclose中
在這裏插入圖片描述
以上是theclose數組的信息,用數組下標來表示邊的另一個點,以第二行爲例表示邊(0,1)的權值爲6,即AB的權值爲6。M設置爲一個很大的int數表示兩點不連通。權值0表示該點已經是生成樹上的點了。

根據建立的theclose找到權值最小的點,設其下標爲i(以上圖爲例i = 2,對應權值1,對應點C,0表示已經在樹上了不參與最小點比較),然後修改其對應權值爲0。此時表示i對應的點也加入到了樹中,因此需要更新新加入的點i,到其他點的權值是否更小。只需要將下標i對應的點到各個點的權值Q與theclose中的權值q進行比對,因爲0表示已經在樹中的點了,所以只需要q>0&&Q<q則可以更新起點下標爲i,權值爲Q。以上圖爲例,可以更新到點E和F的信息
更新後的theclose信息如下
跟新後的點
循環這個過程,找權值最小的點,更新權值。以上圖爲例更新後的信息如下
在這裏插入圖片描述

void miniSpanTreePrim(MGraph G, VertexType u){
	//找到該起始點在頂點數組中的位置下標
	int k = LocateVex(G, u);
	//首先將與該起始點相關的所有邊的信息:邊的起始點和權值,存入輔助數組中相應的位置,
	//例如(1,2)邊,adjvex爲0,lowcost爲6,存入theclose[1]中,輔助數組的下標表示該邊的頂點2
	for (int i = 0; i<G.vexnum; i++) {
		if (i != k) {
			theclose[i].adjvex = k;
			theclose[i].lowcost = G.arcs[k][i].adj;//權值 
			//原先兩點之間沒有聯通 用0表示 現在用INFINITY
		}
	}
	//由於起始點已經歸爲最小生成樹,所以輔助數組對應位置的權值爲0,這樣,遍歷時就不會被選中
	theclose[k].lowcost = 0;
	//選擇下一個點,並更新輔助數組中的信息
	for (int i = 1; i<G.vexnum; i++) {//i = 1? 這裏的i只是表示循環次數 因爲已經找到了一個點k所以
		//總次數-1 ,i = 1;             
		//找出權值最小的邊所在數組下標
		k = minimun(G, theclose);
		//輸出選擇的路徑
		printf("v%d v%d\n", G.vexs[theclose[k].adjvex], G.vexs[k]);//注意這裏的k發生了變化 
		//歸入最小生成樹的頂點的輔助數組中的權值設爲0
		theclose[k].lowcost = 0;
		//信息輔助數組中存儲的信息,由於此時樹中新加入了一個頂點,需要判斷,由此頂點出發,
		//到達其它各頂點的權值是否比之前記錄的權值還要小,如果還小,則更新
		//兩點 1.如新的點與之前的點都能到達同一點的 更新最小權值
		//2.加入新連接的點能到達的點的信息
		for (int j = 0; j<G.vexnum; j++) {
			if (G.arcs[k][j].adj<theclose[j].lowcost) {
				//這裏的0表示已經找過的點 ,INFINITY表示不同的點
				theclose[j].adjvex = k;
				theclose[j].lowcost = G.arcs[k][j].adj;
			}
		}
	}
	printf("\n");
}

依次以上循環直到找到所有的點,因爲每次更新theclose信息的時候都會遍歷其到各點的權值,所以時間複雜度爲n^2,普利姆算法適合密圖。

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