一、最小生成树概念与应用
-
最小生成树的概念 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n个结点,并且有保持图连通的最少的边。
-
最小生成树的性质(MST性质) 假设N=(V,{E}) 是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v∈V-U
,则必存在一棵包含边(u,v)的最小生成树。 -
最小生成树的应用 假设要在n个城市之间建立通信联络网,在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价。要使这 n 个城市的任意两个之间都可以通信,并且总的耗费最少,就要找到带权的最小生成树。
二、普里姆(Prim)算法思路
假设 N= (V,{E})是连通网,TE是N上最小生成树中边的集合。初始令U={uo}(uo∈V),TE={}, 在所有u∈U,v∈V一U的边 (u,v)∈E中找一条代价最小的边 (uo,vo)并入集合TE,同时vo并入U。重复执行上述操作直至U=V为止,则T= (V,{TE})为N的最小生成树。
每次迭代选择代价最小的边对应的,加入到最小生成树中。算法从某一个顶点S开始,逐渐长大覆盖整个连通网的所有顶点。所以此算法也被称为“加点法”。
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<string.h>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int VRType;
typedef int VertexType;
#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 20
typedef struct ArcCell
{
VRType adj;
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM];//节点数值
AdjMatrix arcs;//邻接矩阵
int vexnum, arcnum;//节点数,边数
}MGraph;
typedef struct Array
{
VertexType adjvex;//上一个点
VRType lowcost; //最小权重
}closedge[MAX_VERTEX_NUM];
int LocateVex(MGraph G, VertexType v)
{
int i = 0;
for(i; i < G.vexnum; i++)
{
if(v == G.vexs[i])
{
break;
}
}
if(i < G.vexnum)
{
return i;
}else
{
return G.vexnum ;
}
}
Status CreateUDN(MGraph &G)
{
cout<<"输入顶点数以及边数(以空格分隔):"<<endl;
cin>>G.vexnum>>G.arcnum;
cout<<"请输入顶点信息(以空格分隔):"<<endl;
for(int i = 0; i < G.vexnum; ++i)
{
cin>>G.vexs[i];
}
for(int i = 0; i < G.vexnum; i++)
{
for(int j = 0; j < G.vexnum; j++)
{
G.arcs[i][j] = {INFINITY};
}
}
cout<<"请输入边的信息v1,v2,w(以空格分隔):"<<endl;
for(int k = 0; k < G.arcnum; k++)
{
VertexType v1, v2;
VRType w = INT_MAX;
cin>>v1>>v2>>w;
int i = 0, j = 0;
i = LocateVex(G, v1);
j = LocateVex(G, v2);
if(i == G.vexnum) exit(0);
if(j == G.vexnum) exit(0);
G.arcs[i][j].adj = w;
G.arcs[j][i].adj = G.arcs[i][j].adj;
}
return OK;
}
void PrintMGraph(MGraph G)
{
int i;
for (i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
if (G.arcs[i][j].adj == INT_MAX)
cout << "∞" << " ";
else
cout << G.arcs[i][j].adj << " ";
}
cout << endl;
}
}
int Minimum(closedge C)
{
int i = 0;
int mincost = INT_MAX;
int k = -1;
for(i; i < MAX_VERTEX_NUM; i++)
{
if(C[i].lowcost != 0)
{
if(C[i].lowcost < mincost)
{
mincost = C[i].lowcost;
k = i;
}
}
}
return k;
}
void Prim(MGraph G, VertexType u)
{
closedge closedge = {0, 0};
int k = LocateVex(G, u);
int j = 0;
//初始化辅助矩阵
for(j; j < G.vexnum; j++)
{
if(j != k)
{
closedge[j] = {u, G.arcs[k][j].adj};
}
}
closedge[k].lowcost = 0;
int i = 1;
for(i; i < G.vexnum; i++)
{
k = Minimum(closedge);
cout<<"("<<closedge[k].adjvex<<","<<G.vexs[k]<<")"<<" ";
closedge[k].lowcost = 0;
for(int f = 0; f < G.vexnum; f++)
{
if(G.arcs[k][f].adj < closedge[f].lowcost)
{
closedge[f] = {G.vexs[k], G.arcs[k][f].adj};
}
}
}
}
int main()
{
MGraph G;
CreateUDN(G);
cout<<"矩阵为:"<<endl;
PrintMGraph(G);
cout<<"最小生成树为:"<<endl;
Prim(G, 1);
return 0;
}
运行结果示例:
三、克鲁斯卡尔(Kruskal)算法思路
假设N=(V,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上(即:不形成环),则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。
在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。所以此算法也被称为“加边法”。
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<string.h>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int VRType;
typedef int VertexType;
#define INFINITY INT_MAX
#define MAX_VERTEX_NUM 20
typedef struct ArcCell
{
VRType adj;
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERTEX_NUM];//节点数值
AdjMatrix arcs;//邻接矩阵
int vexnum, arcnum;//节点数,边数
}MGraph;
int LocateVex(MGraph G, VertexType v)
{
int i = 0;
for(i; i < G.vexnum; i++)
{
if(v == G.vexs[i])
{
break;
}
}
if(i < G.vexnum)
{
return i;
}else
{
return G.vexnum ;
}
}
Status CreateUDN(MGraph &G)
{
cout<<"输入顶点数以及边数(以空格分隔):"<<endl;
cin>>G.vexnum>>G.arcnum;
cout<<"请输入顶点信息(以空格分隔):"<<endl;
for(int i = 0; i < G.vexnum; ++i)
{
cin>>G.vexs[i];
}
for(int i = 0; i < G.vexnum; i++)
{
for(int j = 0; j < G.vexnum; j++)
{
G.arcs[i][j] = {INFINITY};
}
}
cout<<"请输入边的信息v1,v2,w(以空格分隔):"<<endl;
for(int k = 0; k < G.arcnum; k++)
{
VertexType v1, v2;
VRType w = INT_MAX;
cin>>v1>>v2>>w;
int i = 0, j = 0;
i = LocateVex(G, v1);
j = LocateVex(G, v2);
if(i == G.vexnum) exit(0);
if(j == G.vexnum) exit(0);
G.arcs[i][j].adj = w;
G.arcs[j][i].adj = G.arcs[i][j].adj;
}
return OK;
}
void PrintMGraph(MGraph G)
{
int i;
for (i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
if (G.arcs[i][j].adj == INT_MAX)
cout << "∞" << " ";
else
cout << G.arcs[i][j].adj << " ";
}
cout << endl;
}
}
typedef struct Arr
{
VertexType strat, end;
VRType weight;
}WEdge[MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1) / 2 + 1];
int Partition(WEdge edge, int low, int high)
{
Arr a = {0, 0, INT_MAX};
a = edge[low];
int pivotkey = edge[low].weight;
while(low<high)
{
while(low<high && edge[high].weight >= pivotkey)
{
--high;
}
edge[low] = edge[high];
while(low<high && edge[low].weight <= pivotkey)
{
++low;
}
edge[high] = edge[low];
}
edge[low] = a;
return low;
}
void QSort(WEdge edge, int low, int high)
{
if(low < high)
{
int pivotloc = Partition(edge, low, high);
QSort(edge, low, pivotloc - 1);
QSort(edge, pivotloc + 1, high);
}
}
void Kruskal(MGraph G)
{
//把边的权重存到数组里
int j = 0;
int k = 0;
int sum = 0;
WEdge edge = {0, 0, INT_MAX};
for(k; k < G.vexnum; k++, j++)
{
for(int i = 0; i < j; i++)
{
if(G.arcs[j][i].adj > 0 && G.arcs[j][i].adj != INT_MAX)
{
edge[sum] = {j, i, G.arcs[j][i].adj};
sum++;
}
}
}
QSort(edge, 0, G.arcnum - 1);
int count3 = 0;
int parents[G.vexnum];
for(count3; count3 < G.vexnum; count3++)
{
parents[count3] = count3;
}
int arcsum = 1;//生成树里边的个数
int count4 = 0;
while(arcsum < G.arcnum)
{
int m1 = edge[count4].strat;
int m2 = edge[count4].end;
int sn1 = parents[m1];
int sn2 = parents[m2];
if(sn1 != sn2)
{
cout<<"("<<G.vexs[m2]<<","<<G.vexs[m1]<<") "<<edge[count4].weight<<" ";
arcsum++;
if(arcsum >= G.vexnum) break;
for(int i = 0; i <= G.vexnum; i++)
{
if(parents[i] == sn2)
{
parents[i] = sn1;
}
}
}
count4++;
}
}
int main()
{
MGraph G;
CreateUDN(G);
cout<<"矩阵为:"<<endl;
PrintMGraph(G);
cout<<"最小生成树为:"<<endl;
Kruskal(G);
return 0;
}
运行结果示例: