1. 程式人生 > >資料結構:最小生成樹--Prim演算法

資料結構:最小生成樹--Prim演算法

                    最小生成樹:Prim演算法

最小生成樹

    給定一無向帶權圖,頂點數是n,要使圖連通只需n-1條邊,若這n-1條邊的權值和最小,則稱有這n個頂點和n-1條邊構成了圖的最小生成樹(minimum-cost spanning tree)。

Prim演算法

    Prim演算法是解決最小生成樹的常用演算法。它採取貪心策略,從指定的頂點開始尋找最小權值的鄰接點。圖G=<V,E>,初始時S={V0},把與V0相鄰接,且邊的權值最小的頂點加入到S。不斷地把S中的頂點與V-S中頂點的最小權值邊加入,直到所有頂點都已加入到S中。

演算法說明

為了方便尋找最小權值的邊,構建一最近邊

結構體CloseEdge:

//最近邊
typedef struct closeedge_tag
{
	int adjvex; //鄰接點
	int weight; //權值
}CloseEdge;
建立一陣列CloseEdge closeedge[n];頂點u屬於S,頂點v屬於V-S,則closeedge[v].weight=min{weight(u,v)};closeedge[v].adjvex=u;另外設定一bool型的陣列add,標記頂點i是否已加入S。結合closeedge和add即可得到當前最小權值邊。每當有新的節點加入S時,則需更新closeedge。具體細節看程式碼。

例項


從V0開始


程式碼

類定義

#include<iostream>  
#include<iomanip>
#include<stack>
using namespace std;
#define MAXWEIGHT 100
//邊
typedef struct edge_tag
{
	int tail;
	int head;
}Edge;
//最近邊
typedef struct closeedge_tag
{
	int adjvex; //鄰接點
	int weight; //權值
}CloseEdge;
class Graph
{
private:
	//頂點數  
	int numV;
	//邊數  
	int numE;
	//鄰接矩陣  
	int **matrix;
public:
	Graph(int numV);
	//建圖  
	void createGraph(int numE);
	//析構方法  
	~Graph();
	//Prim演算法
	void Prim(int);
	int minEdgeVex(CloseEdge*, bool*);
	void updateCloseEdge(CloseEdge*, bool*, int);
	//列印鄰接矩陣  
	void printAdjacentMatrix();
	//檢查輸入  
	bool check(int, int, int);
};

類實現

//建構函式,指定頂點數目
Graph::Graph(int numV)
{
	//對輸入的頂點數進行檢測
	while (numV <= 0)
	{
		cout << "頂點數有誤!重新輸入 ";
		cin >> numV;
	}
	this->numV = numV;
	//構建鄰接矩陣,並初始化
	matrix = new int*[numV];
	int i, j;
	for (i = 0; i < numV; i++)
		matrix[i] = new int[numV];
	for (i = 0; i < numV; i++)
	for (j = 0; j < numV; j++)
	{
		if (i == j)
			matrix[i][i] = 0;
		else
			matrix[i][j] = MAXWEIGHT;
	}
}
void Graph::createGraph(int numE)
{
	/*
	對輸入的邊數做檢測
	一個numV個頂點的有向圖,最多有numV*(numV - 1)條邊
	*/
	while (numE < 0 || numE > numV*(numV - 1))
	{
		cout << "邊數有問題!重新輸入 ";
		cin >> numE;
	}
	this->numE = numE;
	int tail, head, weight, i;
	i = 0;
	cout << "輸入每條邊的起點(弧尾)、終點(弧頭)和權值" << endl;
	while (i < numE)
	{
		cin >> tail >> head >> weight;
		while (!check(tail, head, weight))
		{
			cout << "輸入的邊不正確!請重新輸入 " << endl;
			cin >> tail >> head >> weight;
		}
		//Prim演算法主要針對的是無向圖
		matrix[tail][head] = weight;
		matrix[head][tail] = weight;
		i++;
	}
}
Graph::~Graph()
{
	int i;
	for (i = 0; i < numV; i++)
		delete[] matrix[i];
	delete[]matrix;
}
/*
Prim演算法
求最小生成樹
*/
void Graph::Prim(int vertex)
{
	//有numV個頂點的圖的最小生成樹有numV-1條邊
	Edge *edges = new Edge[numV - 1];
	//標記頂點是否加入
	bool *add = new bool[numV];
	memset(add, 0, numV);
	//先把vertex加入
	add[vertex] = true;
	//最近邊
	CloseEdge *closeedge = new CloseEdge[numV];
	int i;
	//初始化最近邊
	for (i = 0; i < numV; i++)
	{
		closeedge[i].weight = matrix[vertex][i];
		if (!add[i] && matrix[vertex][i] > 0 && matrix[vertex][i] < MAXWEIGHT)
			closeedge[i].adjvex = vertex;
	}
	int v, count = 0;
	while (count < numV - 1)
	{
		//獲取最近邊的鄰接點
		v = minEdgeVex(closeedge, add);
		add[v] = true;
		//把最小權值邊依次加入陣列edges
		edges[count].tail = closeedge[v].adjvex;
		edges[count].head = v;
		//更新最近邊
		updateCloseEdge(closeedge, add, v);
		count++;
	}
	cout << "從頂點 " << vertex << " 開始,最小生成樹的邊是" << endl;
	for (i = 0; i < count; i++)
		cout << edges[i].tail << "---" << edges[i].head << endl;
	//釋放空間
	delete[]edges;
	delete[]add;
	delete[]closeedge;
}
//從closeedge中尋找最小邊的鄰接頂點
int Graph::minEdgeVex(CloseEdge *closeedge, bool *add)
{
	int i, v, w; 
	v = 0;
	w = MAXWEIGHT;
	for (i = 0; i < numV ; i++)
	if (!add[i] && closeedge[i].weight < w)
	{
		w = closeedge[i].weight;
		v = i;
	}
	return v;
}
//頂點v的加入後,需要更新最近邊
void Graph::updateCloseEdge(CloseEdge* closeedge, bool *add, int v)
{
	int i;
	for (i = 0; i < numV; i++)
	if (!add[i] && matrix[v][i] < closeedge[i].weight)
	{
		closeedge[i].adjvex = v;
		closeedge[i].weight = matrix[v][i];
	}
}
//列印鄰接矩陣  
void Graph::printAdjacentMatrix()
{
	int i, j;
	cout.setf(ios::left);
	cout << setw(7) << " ";
	for (i = 0; i < numV; i++)
		cout << setw(7) << i;
	cout << endl;
	for (i = 0; i < numV; i++)
	{
		cout << setw(7) << i;
		for (j = 0; j < numV; j++)
			cout << setw(7) << matrix[i][j];
		cout << endl;
	}
}
bool Graph::check(int tail, int head, int weight)
{
	if ((tail == head) || tail < 0 || tail >= numV 
		|| head < 0 || head >= numV
		|| weight <= 0 || weight >= MAXWEIGHT)
		return false;
	return true;
}
主函式
int main()
{
	cout << "******Prim***by David***" << endl;
	int numV, numE;
	cout << "建圖..." << endl;
	cout << "輸入頂點數 ";
	cin >> numV;
	Graph graph(numV);
	cout << "輸入邊數 ";
	cin >> numE;
	graph.createGraph(numE);
	cout << endl << "Prim..." << endl;
	/*
	由於輸出結果太長,不利於截圖,故只打印一半的節點
	要想獲得從所有節點開始的最小生成樹,修改i的變化範圍即可
	*/
	for (int i = 0; i < numV / 2; i++)
		graph.Prim(i);
	system("pause");
	return 0;
}
執行




若有所幫助,頂一個哦!


專欄目錄: