图的最小生成树有两种算法:
一种是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++语言版》清华大学邓俊辉