最小生成树之Prim算法

算法导论第二十三章 讲解如何求最小生成树


Prim算法是求MST的经典算法之一。


首先,关于MST有伪代码如下


GENERIC-MST(G, w)
1. Set <strong>A</strong> to Empty
2. while <strong>A</strong> does not form a spanning tree
3.         find an edge(u, v) that is safe for <strong>A</strong> 
4.         <strong>A</strong> = Union(<strong>A</strong>, (u, v))
4 . return <strong>A</strong>

Prim算法与Kruskal算法不同在与,Prim算法中A总是构成一棵树。算法的每一步在连接集合A和A之外的结点的 所有边中,选择一天light edge 加入A中。根据推论23.2,这条规则所加入的边都是对A安全的边。

MST-PRIM(G, w, r)
1. for each u belongs to G.V
2.    u:key = INFINITE
3.    u:pi = NIL
4. r:key = 0
5. Q = G.V
6. while Q is not empty
7.     u = EXTRACT-MIN(Q)
8.     for each v belongs to G.Adj[u]
9.         if v belongs to Q and w(u, v) < v.key
10.				v.pi = u
11.				v.key = w(u, v)

为了有效地实现Prim算法, 需要一种快速的方法来选择一条新的边,以便加入到由集合A中的边所构成的树里。在算法的执行过程中,所有不在树A中的结点都存放在一个基于key属性的最小优先队列Q中。key属性保存的是该结点与树中结点的所有边中最小边的权重。

在实际的实现中,对于算法的第九行,如何迅速地找到v.key
和第十一行,如何改变v.key在优先队列的值。

C++并没有提供decrease_key操作,所以我的实现是通过加入边来实现。

具体如下
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

struct Edge
{
	Edge(size_t u, size_t v, int weight)
	{
		this->u = u;
		this->v = v;
		this->weight = weight;
	}
	size_t u;
	size_t v;
	int weight;
	bool operator>(const Edge &rhs) const
	{
		return weight > rhs.weight;
	}
};

class EdgeCom
{
public:
	bool operator()(const Edge& lhs, const Edge& rhs) const
	{
		return lhs > rhs;
	}
};



class Prim
{
public:
	Prim(size_t, size_t);
	void addEdge(size_t, size_t, int);
	void run();
	void showResult();

private:
	priority_queue<Edge, vector<Edge>, EdgeCom > Q;
	vector<vector<int> > map;
	size_t V,E;
	
	int totalWeight;

	vector<Edge> treeEdge;

	vector<bool> onQueue;

};

Prim::Prim(size_t v, size_t e):map(v, vector<int>(v, INT_MAX)), onQueue(v, true)
{
	this->V = v;
	this->E = e;
	totalWeight = 0;
}

void Prim::addEdge(size_t u, size_t v, int weight)
{
	map[u][v] = weight;
	map[v][u] = weight;
}

void Prim::run()
{
	size_t start  = 0;
	onQueue[start] = false;
	size_t cnt = 1;

	for(size_t i = 0;i<V;i++)
	{
		if(map[start][i] != INT_MAX)
		{
			Q.push(Edge(start, i, map[start][i]) );
		}

	}
	
	while(cnt < V)
	{
		Edge x = Q.top();
		Q.pop();
		size_t cur;
		if( onQueue[x.u] == true )
		{
			cur=x.u;
		}
		else if(onQueue[x.v] == true )
		{
			cur=x.v;
		}
		else
		{
			continue;
		}
	
		onQueue[cur] = false;
		treeEdge.push_back(x);
		totalWeight += x.weight;
		for(size_t i = 0;i<V;i++)
		{
			if(map[cur][i] != INT_MAX)
			{
				Q.push(Edge(cur, i, map[cur][i]) );
			}

		}

		/*size_t u = x.u;
		size_t v = x.v;
		
		if( onQueue[u] == true )
		{
			onQueue[u] = false;
			treeEdge.push_back(x);
			totalWeight += x.weight;
			for(size_t i = 0;i<V;i++)
			{
				if(map[u][i] != INT_MAX)
				{
					Q.push(Edge(u, i, map[u][i]) );
				}

			}
		}
		else if (onQueue[v] == true )
		{
			onQueue[v] = false;
			treeEdge.push_back(x);
			totalWeight += x.weight;
			for(size_t i = 0;i<V;i++)
			{
				if(onQueue[i]==true && map[v][i] != INT_MAX)
				{
					Q.push(Edge(v, i, map[v][i]) );
				}

			}
		}
		else
		{
			continue;
		}*/
		cnt++;
	}	
}

void Prim::showResult()
{
	cout<<totalWeight<<endl;
	cout<<"u\tv\tweight"<<endl;
	for(size_t i = 0;i<V-1;i++)
	{
		cout<<treeEdge[i].u<<"\t"<<treeEdge[i].v<<"\t"<<treeEdge[i].weight<<endl;
	}
}

int main()
{
	int V, E, u, v, weight; 
	cin>>V>>E;
	Prim p(V, E);
	for(size_t i=0;i<E;i++)
	{
		cin>>u>>v>>weight;
		p.addEdge(u,v,weight);
	}

	p.run();
	p.showResult();

	cout<<"EOF"<<endl;
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值