图
最小代价生成树定义
在含n个顶点的无向连通图G中,若存在由n-1条边连通n个顶点的子图G',
称G'为G的一棵生成树(spanning tree).
若果无向连通图的每一条边都赋了一个权值,则称此图为网络(network).
对于网络,如何寻找一棵生成树使得各条边的权值之和最小,是一个问题.
在一个网络的各种生成树中,具有最小代价的生成树称为最小代价生成树(minimum cost spanning tree)
简称最小生成树(minimum spanning tree 即MST)
Prim算法
设网络G={V,E},V={0,1,2,,,n-1}.
设U为V的子集,然后从V-U的集合中找出一顶点x,
该顶点x与U集合中的某点之间的边是最小权值边且不会造成回路.
然后将顶点x加入U集合中,反复执行同样的步骤,直到U=V为止.
#include<iostream>
using namespace std;
#define MaxSize 20
#define inf 2147483647
//图定义
struct Graph{
int V[MaxSize]; //存储顶点的数组
int M[MaxSize][MaxSize]; //存储边的数组
int n; //图所含顶点个数
};
//根据顶点集V 边集合E 创建图的领接矩阵
void Create(Graph &G,int *V,int vn,int *E,int en,int action){
int i,j,k,w;
G.n=vn;
for(i=0;i<G.n;i++)
G.V[i]=V[i];
for(i=0;i<G.n;i++){
for(j=0;j<G.n;j++)
if(i==j)
G.M[i][j]=0;
else
G.M[i][j]=inf;
}
for(k=0;k<en;k++){
i=E[3*k+0];
j=E[3*k+1];
w=E[3*k+2];
G.M[i][j]=w;
if(action==0) //如果是无向图
G.M[j][i]=w;
}
}
//Prim算法 图的最小生成树
void Prim(Graph G,int sv){
int UC[2][MaxSize];
int newUC[2][MaxSize];
int i,j,k,n,min,col,choice;
n=G.n; //图所含顶点个数
for(j=0;j<n;j++){
UC[0][j]=inf; //置U到V-U的初始代价为inf
UC[1][j]=-1; //置U为空集
}
k=sv; //取起始顶点sv进入U集
choice=1; //挑选边数
while(choice<n){
for(i=0;i<2;i++)
for(j=0;j<n;j++)
if(i==0)
newUC[i][j]=G.M[k][j];//据带权邻接矩阵给出顶点k到j的代价
else
newUC[i][j]=k; //顶点k进入U集
//比较newUC与UC对应列的代价,如果newUC中代价小于等于UC中代价
//则用newUC中的列置换UC中的对应列
for(j=0;j<n;j++)
if(newUC[0][j]<=UC[0][j]){
UC[0][j]=newUC[0][j]; //newUC的第j列置换UC的第j列
UC[1][j]=newUC[1][j];
}
//寻找UC中除了0之外的最小代价所在列
min=inf;col=0;
for(j=0;j<n;j++)
if(UC[0][j]<min&&UC[0][j]!=0){
min=UC[0][j]; //顺序搜索最小代价所在列
col=j;
}
//输出挑选的最小生成树的边与权值
cout<<"边("<<UC[1][col]<<","<<col<<"),权值"<<min<<endl;
//再取进入U集顶点,进入下一轮挑选
k=col;
choice++;
}
}
//输出邻接矩阵
void Display(Graph G){
int i,j;
cout<<"顶点"<<"\t";
for(i=0;i<G.n;i++)
cout<<"V"<<G.V[i]<<"\t";
cout<<endl;
for(i=0;i<G.n;i++){
cout<<"V"<<G.V[i]<<"\t";
for(j=0;j<G.n;j++)
if(G.M[i][j]==inf)
cout<<"inf"<<"\t";
else
cout<<G.M[i][j]<<"\t";
cout<<endl;
}
}
int main(){
int V[6]={0,1,2,3,4,5};
int E[10][3]={{0,1,6},{0,5,10},{0,4,12},{1,2,3},{1,3,5},{1,4,8},{2,3,7},{3,4,11},{3,5,9},{4,5,16}};
Graph G;
Create(G,&V[0],6,&E[0][0],10,0);
cout<<"图的领接矩阵"<<endl;
Display(G);
cout<<"最小代价生成树"<<endl;
Prim(G,0);
Kruskal算法
kruskal算法是将所有边按权值由小到大排序,
然后从权值最小的边开始挑选起,构架最小生成树,
若加入的边会造成回路则舍弃不用,直到所有边被挑选完为止.
#include<iostream>
using namespace std;
#define MaxSize 20
#define inf 2147483647
//带权边的定义
struct Edge{
int x,y,w; //边的起点,终点,权值
};
//图定义
struct Graph{
int V[MaxSize]; //顶点
Edge E[MaxSize]; //带权边
int n; //顶点个数
int e; //边数
};
//根据顶点集V 边集合E 创建图的领接矩阵
void Create(Graph &G,int *V,int vn,int *E,int en){
G.n=vn;
G.e=en;
for(int i=0;i<G.n;i++)
G.V[i]=V[i];
for(int k=0;k<en;k++){
G.E[k].x=E[3*k+0];
G.E[k].y=E[3*k+1];
G.E[k].w=E[3*k+2];
}
}
//输出邻接矩阵
void Display(Graph G){
cout<<"起点"<<"\t"<<"终点"<<"\t"<<"权值"<<endl;
for(int i=0;i<G.e;i++)
cout<<G.E[i].x<<"\t"<<G.E[i].y<<"\t"<<G.E[i].w<<endl;
}
//选择排序
void Sort(Graph &G){
int i,j,min,pos,flag;
Edge temp;
for(i=0;i<G.e-1;i++){
min=G.E[i].w;
pos=i;
flag=1;
for(j=i+1;j<G.e;j++){
if(min>G.E[j].w){
min=G.E[j].w,pos=j;flag=0;
}
}
if(flag==0){
temp=G.E[i];
G.E[i]=G.E[pos];
G.E[pos]=temp;
}
}
}
//Kruskal算法
void MST(Graph G){
int i,j,m1,m2,n1,n2;
for(i=0;i<G.e;i++){
m1=G.E[i].x;
m2=G.E[i].y;
n1=G.V[m1];
n2=G.V[m2];
if(n1!=n2){
cout<<"边("<<m1<<","<<m2<<"),代价:"<<G.E[i].w<<endl;
for(j=0;j<G.e;j++)
if(G.V[j]==n2)
G.V[j]=n1;
}
}
}
int main(){
int V[6]={0,1,2,3,4,5};
int E[10][3]={{0,1,6},{0,5,10},{0,4,12},{1,2,3},{1,3,5},{1,4,8},{2,3,7},{3,4,11},{3,5,9},{4,5,16}};
Graph G;
Create(G,&V[0],6,&E[0][0],10);
Sort(G);
cout<<"带权图的边排序"<<endl;
Display(G);
cout<<"最小代价生成树"<<endl;
MST(G);
}
写于2020-10-26