我们先来了解一下什么是生成树?
1.生成树:在图G中,包含G中所有顶点且不构成回路的极小连通图
那么最小生成树(MST)便是一个代价最小的生成树
我们如何构建最小生成树呢?正常思维便是一步一步找到代价最小的一部分最后达到全局的最小【有贪心算法的思想】
接下来,我们介绍两种算法,kruscal算法和prim算法。
kruskal算法
思想:一开始每个顶点分属不同集合【先这么理解】,每次摘选权值最小的边及其两端顶点,将此两顶点归为同一集合,重复操作,过程中避免形成环路,直至所有顶点都在同一个集合中。
难点:避免形成环路
我们先来思考什么样的存储结构适合kruskal,每次都要选取两顶点一边,不论是邻接矩阵还是邻接表都有点麻烦,因此我们采用一种新的结构
typedef struct EdgeType{
int v1,v2,w;//点v1,点v2,及权值
}EdgeType;
typedef struct Graph{
EdgeType edge[100];
int vexnum,edgenum;
}Graph;
而针对避免环路,我们设定一个parent数组,用于记录每个顶点的根节点,根节点起初都是-1,根节点不一样或-1才进行合并
代码实现:
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef struct EdgeType{
int v1,v2,w;//点v1,点v2,及权值
}EdgeType;
typedef struct Graph{
EdgeType edge[100];
int vexnum,edgenum;
}Graph;
int parent[100];
void kruskal(Graph &G);//图G
int findRoot(int parent[],int v);//求根节点
void outputMST(EdgeType edge);//输出
void kruskal(Graph &G){
for(int i=0;i<G.edgenum;++i){
parent[i]=-1;
}
for(int num=0,i=0;i<G.edgenum;++i){
int vex1=findRoot(parent,G.edge[i].v1);
int vex2=findRoot(parent,G.edge[i].v2);
if(vex1!=vex2){
outputMST(G.edge[i]);
parent[vex2]=vex1;
num++;
if(num==G.edgenum) return;
}
}
}
//parent值为-1时,t自身就是根节点;非-1时,parent值为根节点
int findRoot(int parent[],int v){
while(parent[v]>-1){
v=parent[v];
}
return v;
}
void outputMST(EdgeType edge){
cout<<"("<<edge.v1<<" "<<edge.v2<<") 权值:"<<edge.w<<endl;
}
int cmp(const void *v1,const void *v2){
EdgeType *x=(EdgeType*) v1;
EdgeType *y=(EdgeType*) v2;
return x->w-y->w;
}
int main(){
Graph G;
cin>>G.vexnum>>G.edgenum;
for(int i=0;i<G.edgenum;++i){
cin>>G.edge[i].v1>>G.edge[i].v2>>G.edge[i].w;
}
qsort(G.edge,G.edgenum,sizeof(EdgeType),cmp);
kruskal(G);
return 0;
}
prim算法:
思想:与kruskal不同的是,prim算法是从某一个顶点处罚,根据边来摘选最小,直到构成生成树
代码实现:
#include <iostream>
using namespace std;
#define maxint 32767
#define max 100
typedef int vextype;
typedef int edgetype;
//邻接矩阵
typedef struct graph{
vextype vexs[max];
edgetype edge[max][max];
int vexnum,edgenum;
}graph;
typedef struct shortedge{
vextype adjvex;
edgetype lowcost;
}shortedge;
shortedge s[100];
void creategraph(graph &g);
void prim(graph &g,int start);
int minedge(shortedge s[],int vexnum);
void outputMST(int a,shortedge b);
int locate(graph &g,vextype v);
void creategraph(graph &g){
cin>>g.vexnum>>g.edgenum;
for(int i=0;i<g.vexnum;++i){
cin>>g.vexs[i];
}
for(int i=0;i<g.edgenum;++i){
for(int j=0;j<g.edgenum;++j){
g.edge[i][j]=maxint;
}
}
for(int i=0;i<g.edgenum;++i){
vextype v1,v2;
edgetype w;
cin>>v1>>v2>>w;
int x=locate(g,v1);
int y=locate(g,v2);
g.edge[x][y]=w;
g.edge[y][x]=g.edge[x][y];
}
}
int locate(graph &g,vextype v){
for(int i=0;i<g.vexnum;++i){
if(v==g.vexs[i]) return i;
}
return -1;
}
void prim(graph &g,int start){
for(int i=0;i<g.vexnum;++i){
s[i].lowcost=g.edge[start][i];//把初始权值付给shortage数组
s[i].adjvex=start;//把初始顶点赋给shortage数组,代表是当前点到各边
}
s[start].lowcost=0;//进行同化,把起点start放入集合U
for(int i=0;i<g.vexnum-1;++i){
int k = minedge(s,g.vexnum);//寻找最短边的邻接点k返回其下标
outputMST(k,s[k]);//输出当前最短路径 【k--相当于邻接点位置;s[k]--相当于包含了vex即要到k的点和cost即他俩之间的权值】
s[k].lowcost=0;//将k也进行同化
for(int j=0;j<g.vexnum;++j){
if(g.edge[k][j]<s[j].lowcost){
s[j].lowcost=g.edge[k][j];
s[j].adjvex=k;
}
}
}
}
int minedge(shortedge *s,int vexnum){
shortedge min_s;
min_s.lowcost=32767,min_s.adjvex=0;
for(int i=0;i<vexnum;++i){
if(s[i].lowcost!=0)//等于0代表已被同化
if(min_s.lowcost>s[i].lowcost){
min_s.lowcost=s[i].lowcost;
min_s.adjvex=i;
}
}
return min_s.adjvex;
}
void outputMST(int a,shortedge b){
cout<<b.adjvex+1<<" "<<a+1<<" 权值:"<<b.lowcost<<endl;
}
int main(){
graph g;
creategraph(g);
prim(g,0);
return 0 ;
}