1. 程式人生 > >prim 演算法 c++實現

prim 演算法 c++實現

1.概述

  設G =(V,E)是無向連通帶權圖,即一個網路。E中每條邊(v,w)的權為c[v][w]。如果G的子圖G’是一棵包含G的所有頂點的樹,則稱G’為G的生成樹。生成樹上各邊權的總和稱為該生成樹的耗費。在G的所有生成樹中,耗費最小的生成樹稱為G的最小生成樹

     網路的最小生成樹在實際中有廣泛應用。例如,在設計通訊網路時,用圖的頂點表示城市,用邊(v,w)的權c[v][w]表示建立城市v和城市w之間的通訊線路所需的費用,則最小生成樹就給出了建立通訊網路的最經濟的方案。

  最小生成樹性質:

   設G=(V,E)是連通帶權圖,U是V的真子集。如果(u,v)屬於E,且u屬於U,v屬於V-U,且在所有這樣的邊中,(u,v)的權c[u][v]最小,那麼一定存在G的一棵最小生成樹,它以(u,v)為其中一條邊。這個性質有時也稱為MST性質。

2.PRIM演算法

  設G=(V,E)是連通帶權圖,V={1,2,…,n}。

  構造G的最小生成樹的Prim演算法的基本思想是:首先置S={1},然後,只要S是V的真子集,就作如下的貪心選擇:選取滿足條件i屬於S,j屬於V-S,且c[i][j]最小的邊,將頂點j新增到S中。這個過程一直進行到S=V時為止。

  在這個過程中選取到的所有邊恰好構成G的一棵最小生成樹。

  利用最小生成樹性質和數學歸納法容易證明,上述演算法中的邊集合T始終包含G的某棵最小生成樹中的邊。因此,在演算法結束時,T中的所有邊構成G的一棵最小生成樹。

  例如,對於右圖中的帶權圖,按Prim演算法選取邊的過程如下頁圖所示。  

按Prim演算法選取邊的過程如下頁圖所示。

  在上述Prim演算法中,還應當考慮如何有效地找出滿足條件i在S中,j也在V-S中,且權c[i][j]最小的邊(i,j)。實現這個目的的較簡單的辦法是設定2個數組closest和lowcost。

  在Prim演算法執行過程中,先找出V-S中使lowcost值最小的頂點j,然後根據陣列closest選取邊(j,closest[j]),最後將j新增到S中,並對closest和lowcost作必要的修改。

  用這個辦法實現的Prim演算法所需的計算時間為O(n2).

       其實這個演算法和迪傑斯特拉演算法非常相似,都是每次都要取目前已知的到達未連通結點的最小權值,當加入該點之後再重新整理lowcost陣列和closest陣列,對比看是否未加入樹的點通過該剛加入樹的點是否有更短的路徑也就是更小的權值,若有則重新整理,若沒有則不處理,以保證lowcost和closest數組裡的資料都是根據當前已知情況得到的最小權值陣列和該頂點最接近的頂點編號。

圖是搬運的,因為自己懶得做。

程式碼如下:

#include<iostream>
#include<vector>
using namespace std;
void prim(vector<vector<int>> &VGraph, vector<int> &lowcost, vector<int> &closest, vector<bool> &visited)  //思路類似於迪傑斯特拉演算法
{
	int size = lowcost.size();
	visited[0] = true;
	for (int i = 1; i < size; i++)
	{
		lowcost[i] = VGraph[0][i];
		closest[i] = 0;
		visited[i] = false;
	}
	cout << "0";
	int weight = 0;
	for (int i = 0; i < size; i++)
	{
		int min = 99999;
		int index = 1;
		for (int j = 0; j < size; j++)
		{
			if (lowcost[j] < min&&!visited[j])
			{
				min = lowcost[j];
				index = j;
			}
		}
		if (index == 1 && min == 99999)
		{
			cout << "\n最小生成樹權值為:" << weight<<endl;
			return;
		}
		else
		{
			weight += min;
		}
		cout << " -> " << index;
		visited[index] = true;
		for (int j = 1; j <size; j++)  //因為新加入了j點,所以要查詢新加入的j點到未在S中的點K中的權值是不是可以因此更小
		{
			if ((VGraph[index][j]<lowcost[j]) && (!visited[j]))  //lowcost表示從已知樹到某一點所需付出的最小權值
			{ 
				lowcost[j] = VGraph[index][j];
				closest[j] = index;
			}
		}
	}
}
int main()
{
	int M, N;
	cin >>M>>N;
	vector<vector<int>> VGraph(M);
	vector<int> lowcost(M);
	vector<int> closest(M);
	vector<bool> visited(M);
	for (int i = 0; i < M; i++)
	{
		VGraph[i].resize(M);
	}
	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < M; j++)
		{
			VGraph[i][j] = 99999;
		}
	}
	for (int i = 0; i < N; i++)
	{
		int a, b;
		cin >> a >> b;
		int length;
		cin >> length;
		VGraph[a][b] = VGraph[b][a] = length;
	}
	prim(VGraph, lowcost, closest, visited);
}