生成树:包含全部顶点的一个极小连通子图
最小生成树:代价(权值和)最小的生成树
算法:Kruskal算法
输入:无向连通网G=(V,E)
输出:最小生成树T=(U,TE)
1. 初始化:U=V;TE={ }
2. 重复下述操作直到所有顶点位于一个连通分量中
3.在E中选取最短边(u,v),
- 如果顶点 u、v 位于两个连通分量,则将边 (u,v) 并入TE,并将这两个连通分量合成一个连通分量
- 如果顶点 u、v 位于一个连通分量,在 E 中标记边 (u,v),使得 (u,v) 不参加后续最短边的选取
图的存储结构:边集数组
合并连通分量:并查集
并查集:集合中的元素组织成树的形式:
(1)查找两个元素是否属于同一集合:所在树的根结点是否相同
(2)合并两个集合:将一个集合的根结点作为另一个集合根结点的孩子
#include <iostream>
using namespace std;
struct EdgeType{//边集数组
int from,to,weight;//起点,终点,权值
};
class EdgeGraph{
public:
EdgeGraph(char a[],int n,int e);
~EdgeGraph();
void Kruskal();
int FindRoot(int parent[],int v);//求顶点v所在集合的根
private:
char vertex[10];
EdgeType edge[100];
int vertexNum,edgeNum;
};
EdgeGraph::EdgeGraph(char a[],int n,int e){
int i,j,k,w;
vertexNum=n;edgeNum=e;
for(i=0;i<vertexNum;i++)
vertex[i]=a[i];
for(k=0;k<edgeNum;k++){
cout<<"请输入边的两个顶点编号和边的权值:";
cin>>i>>j>>w;
edge[k].from=i;edge[k].to=j;edge[k].weight=w;
}
}
EdgeGraph::~EdgeGraph(){}
void EdgeGraph::Kruskal(){
int num=0,i,vex1,vex2;
int parent[vertexNum];//双亲表示法存储并查集
for(i=0;i<vertexNum;i++)
parent[i]=-1;//初始化没有双亲,置-1
for(num=0,i=0;num<vertexNum-1;i++){//在已排好序的边集数组中找最短边
vex1=FindRoot(parent,edge[i].from);
vex2=FindRoot(parent,edge[i].to);
if(vex1!=vex2){//分属两个集合
cout<<"("<<edge[i].from<<","<<edge[i].to<<")"<<" "<<edge[i].weight<<endl;
parent[vex2]=vex1;//合并集合
num++;
}
}
}
EdgeGraph::FindRoot(int parent[],int v){
int t=v;
while(parent[t]>-1)
t=parent[t];//求顶点t的双亲一直到根
return t;
}
//(1 4 12)(2 3 17)(0 5 19) (2 5 25)(3 5 25)(4 5 26)(0 1 34)(3 4 38)(0 2 46)
int main()
{
char ch[6]={'A','B','C','D','E','F'};
EdgeGraph eg(ch,6,9);
eg.Kruskal();
return 0;
}
运行结果
请输入边的两个顶点编号和边的权值:1 4 12
请输入边的两个顶点编号和边的权值:2 3 17
请输入边的两个顶点编号和边的权值:0 5 19
请输入边的两个顶点编号和边的权值:2 5 25
请输入边的两个顶点编号和边的权值:3 5 25
请输入边的两个顶点编号和边的权值:4 5 26
请输入边的两个顶点编号和边的权值:0 1 34
请输入边的两个顶点编号和边的权值:3 4 38
请输入边的两个顶点编号和边的权值:0 2 46
(1,4) 12
(2,3) 17
(0,5) 19
(2,5) 25
(4,5) 26