图的最小生成树算法

图的最小生成树有两种算法:

一种是Kruscal算法:把图的每个节点看成是不同的集合,把所有的边都放入到优先级队列中去,不断从优先级队列中取出边,如果这个边的两端点属于不同的集合,那么就把这条边加入到最小生成树的边,并且把这两个集合合并,如果这两个边的端点属于同一集合,则相当于是环状,就丢弃当前这条边。

用到的主要的数据结构就是优先级队列和一个简单的并查集,优先级队列是用的STL标准库,是用vector和heap作为辅助结构实现的,而并查集是一个很经典的集合数据结构,其中每个集合都有一个唯一的代表元素,如果比较两个集合是不是同一个集合,只需要判断两个集合的代表元素是否相同即可。如果需要合并两个集合,只需要将两个集合的代表元素设置成相同的即可。算法的复杂度为:O(ElogV)

另一种是prim算法:把图中的第一个定点认为是树根或子图,将子图中的边入队到优先级队列中,不断从队列中挑选最小的边,将其另一端所指向的节点加入到子图中去,同时把和这个节点相关的边加入到优先级队列中。知道队列中为空位置。

用到的数据结构是优先级队列,算法的复杂度为:O(VlogV+ElogV)=O(ElogV)。

代码:

//GraphMatrix.h
#ifndef __GraphMatrix__
#define __GraphMatrix__
#include<iostream>
#include<vector>
using namespace std;
struct Vertex
{
	char data;
	bool visited;
	Vertex* p;//kruskal算法要用
	int rank;//kruskal算法要用
	Vertex(char d=0)
	{
		rank=1;
		data=d;
		visited=false;
	}
};

struct Edge
{
	int weight;
	Edge(int w=INT_MAX)
	{
		weight=w;
	}
};

struct Node
{
	char from;
	char to;
	int weight;
	Node(char f,char t,int w=INT_MAX)
	{
		from=f;
		to=t;
		weight=w;
	}
	bool operator < (const Node &a) const
	{
		return weight>a.weight;
	}
};

struct KruscalNode
{
	int from;
	int to;
	int weight;
	KruscalNode(int f,int t,int w=INT_MAX)
	{
		from=f;
		to=t;
		weight=w;
	}
	bool operator < (const KruscalNode &a) const
	{
		return weight>a.weight;
	}
};

class GraphMatrix
{
	int n;
	int e;
	vector<Vertex> V;//顶点集合
	vector<vector<Edge> >E;//边的集合
	Vertex* findSet(Vertex *node);
	void unionSet(Vertex &node1,Vertex &node2);
public:
	GraphMatrix()
	{
		n=e=0;
	}
	int insertVertex(Vertex & v);
	void insertEdge(Vertex from,Vertex to,Edge e);
	void prime(vector<Vertex> &result_V,vector<Node> &result_E);
	void kruskal(vector<Node> &result_E);
	
};
#endif

//GraphMatrix.cpp
#include"GraphMatrix.h"
#include<queue>
using namespace std;

int GraphMatrix::insertVertex(Vertex & v)
{
	V.push_back(v);
	E.push_back(vector<Edge>(V.size()));
	if(E.size()>1)
		for(int i=0;i<V.size()-1;i++)
			E[i].push_back(Edge());
	n++;
	return V.size()-1;
}
void GraphMatrix::insertEdge(Vertex from,Vertex to,Edge e)
{
	int k=0;
	int p=-1,q=-1;
	for(int i=0;i<V.size();i++)
	{
		if(V[i].data==from.data)
		{
			k+=1;
			p=i;
		}
		if(V[i].data==to.data)
		{
			k+=2;
			q=i;
		}
	}
	if((k&1)==0)
		p=insertVertex(from);
	if((k&2)==0)
		q=insertVertex(to);
	E[p][q]=e;
	E[q][p]=e;//如果不屏蔽则是无向图
	this->e++;
}

void GraphMatrix::prime(vector<Vertex> &result_V,vector<Node> &result_E)
{
	priority_queue<Node> pq;
	pq.push(Node(V[0].data,V[0].data,0));//先把第一个点放入优先级队列中
	while(!pq.empty())
	{
		Node temp=pq.top();
		int k=-1;
		for(int i=0;i<V.size();i++)//其实可以把pq定义成为一个存储3个int型的变量,来去掉这个for循环
		{						   //即node temp=pq.front(),访问V[temp.second].isvisited即可
			if(V[i].data==temp.to) //这里为了方便存储边,就没有用这个方法。
			{
				if(V[i].visited==false)
					k=i;//即开始访问第k个节点
				break;
			}
		}
		pq.pop();
		if(k==-1)
			continue;
		result_V.push_back(V[k]);
		V[k].visited=true;
		result_E.push_back(temp);
		for(int i=0;i<E[k].size();i++)
		{
			if(E[k][i].weight!=INT_MAX && V[i].visited==false)
				pq.push(Node(V[k].data,V[i].data,E[k][i].weight));
		}
	}
}

void GraphMatrix::kruskal(vector<Node> &result_E)
{
	priority_queue<KruscalNode> pq;
	for(int i=0;i<E.size();i++)//创建边的优先级队列
	{
		V[i].p=&V[i];
		for(int j=0;j<E.size();j++)
		{
			if(E[i][j].weight!=INT_MAX)
			{
				pq.push(KruscalNode(i,j,E[i][j].weight));
			}
		}
	}
	unionSet(V[pq.top().from],V[pq.top().to]);//把前两个点作为一个集合
	result_E.push_back(Node(V[pq.top().from].data,V[pq.top().to].data,pq.top().weight));
	pq.pop();
	while(!pq.empty())
	{
		KruscalNode temp=pq.top();
		if(findSet(&V[temp.from])!=findSet(&V[temp.to]))
		{
			unionSet(V[temp.from],V[temp.to]);
			result_E.push_back(Node(V[temp.from].data,V[temp.to].data,temp.weight));
		}
		pq.pop();
	}
}

Vertex* GraphMatrix::findSet(Vertex *node)
{
	if(node->p!=node)
		node->p=findSet(node->p);
	return node->p;
}

void GraphMatrix::unionSet(Vertex &node1,Vertex &node2)
{
	Vertex *p1=findSet(&node1);
	Vertex *p2=findSet(&node2);
	if(p1->rank>p2->rank)
		p2->p=&(*p1);
	else
	{
		p1->p=&(*p2);
		if(node1.rank==node2.rank)
			node2.rank++;
	}
}


#include<iostream>
#include"GraphMatrix.h"
#include<queue>
#include<deque>
using namespace std;

#define DEBUG

int main()
{
#ifdef DEBUG
	freopen("input.txt","r",stdin);
#endif
	GraphMatrix gm;
	char from,to;
	int e;
	//测试边和顶点的插入
	cout<<"输入边"<<endl;
	while(cin>>from>>to>>e)
	{
		cout<<"输入边"<<endl;
		gm.insertEdge(from,to,e);//调用默认构造函数
	}
	vector<Vertex> result_V;
	vector<Node> result_E;
	gm.prime(result_V,result_E);
	vector<Node> result_E1;
	gm.kruskal(result_E1);

	system("pause");
	return 0;
}
测试数据为:

A B 4
A H 8
B H 11
B C 8
C D 7
D E 9
D F 14
E F 10
C F 4
C I 2
F G 2
I G 6
H G 1
I H 7
参考:《算法导论》、《数据结构 C++语言版》清华大学邓俊辉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值