0
点赞
收藏
分享

微信扫一扫

【最小生成树问题(Minimum Spanning Tree)——图】



最小生成树问题(Minimum Spanning Tree)——图


  • ​​1、最小生成树(Minimum Spanning Tree)​​
  • ​​2、最小生成树的典型用途​​
  • ​​3、最小生成树的求解​​

  • ​​·普里姆算法(Prim)​​
  • ​​·克鲁斯卡尔算法(Kruskal)​​



1、最小生成树(Minimum Spanning Tree)

​目标​:在网的多个生成树中,寻找一个各边权值之和最小的生成树,即最小生成树。

​构造最小生成树的准则​:
1、必须只使用该网中的边来构造最小生成树
2、必须使用且仅使用n-1条边来联结网络中的n个顶点
3、不能使用产生回路的边

2、最小生成树的典型用途

欲在n个城市间建立通信网,则n个城市应铺n-1条线路,但因为每条线路都会有对应的经济成本,而n个城市可能有n(n-1)/2条线路,那么,如何选择n-1条线路,使总费用最少?

数学模型:
顶点:表示城市,有n个
边:表示线路,有n-1条
边和权值:表示线路的经济代价

【最小生成树问题(Minimum Spanning Tree)——图】_sed

3、最小生成树的求解

·普里姆算法(Prim)

​1、普里姆算法的基本思想:加点法​

设N = (V,{E})使连通网,TE是N上最小生成树中边的集合。

Prim算法思想的构造过程:

【最小生成树问题(Minimum Spanning Tree)——图】_算法_02

​2、设计数据结构​

(1)图采用邻接矩阵来存储
(2)一维数组closedeg,记录从U到V-U具有最小代价的边。

【最小生成树问题(Minimum Spanning Tree)——图】_sed_03

/*图的邻接矩阵存储表示法*/
//用两个数组分别存储顶点表和邻接矩阵
#define MaxInt 32767 //表示极大值,即无穷
#define MVNum 100 //最大顶点数
typedef char VerTexType; //假设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
typedef struct
{
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum, arcnum; //图的当前顶点数和边数
}AMGraph;

对每个顶点v,V-U在辅助数组存在一个相应的分量closedge[i-1],它包括两个域:

typedef struct
{
VerTexType adjvex;//最小边的顶点
ArcType lowcost;//最小边的权值
}closedge[MAX_VERTEX_NUM];
//adjvex:依附于这条最小代价边的另一个顶点
//lowcost = 0 :表示顶点已经在顶点集U中
//lowcost > 0 :表示顶点i还在V-U中

所以,每次循环须在lowcost >0(在集合V-U中)的那些顶点中选择lowcost最小的顶点加入到集合中,同时将相关顶点的closedge作相应的调整。

​3、Prim算法描述​

void MiniSpanTree_Prim(AMGraph G, VerTexType u)
{//无向网G以邻接矩阵存储,从顶点u出发构造G的最小生成树T,输出T的各条边
k = LocateVex(G, u);//起点位置,k为顶点u的下标
for (j = 0;j < G.vexnum;++j)//对V-U的每个顶点vi,初始化closedge[i]
{
if (j != k)
{
closedge[j].adjvex = u;
closedge[j].lowcost = G.arcs[k][j];
}
}
closedge[k].lowcost = 0;//初始,U = { u }
for (i = 1;i < G.vexnum;++i)
{//选择其余n-1个顶点,生成n-1条边(n = G.vexnum )
k = Min(closedge);//求出T的下一个结点:closedge[k]存有当前最小边
u0 = closedge[k].adjvex;//u0为最小边的一个顶点,u0 属于 U
v0 = G.vexs[k];//v0为最小边的另一个顶点,v0 属于 V-U
cout << "边" << u0 << "-->" << v0 << endl;//输出当前的最小边(u0,v0)
closedge[k].lowcost = 0;//第k个顶点并入U集
for(j=0;j<G.vexnum;++j)
if (G.arcs[k][j] < closedge[j].lowcost)
{//新顶点并入U后重新选择最小边
closedge[j].adjvex = G.vexs[k];
closedge[j].lowcost = G.arcs[k][j];
}
}
}

·克鲁斯卡尔算法(Kruskal)

​1、克鲁斯卡尔Kruskal算法的基本思想:加边法​

有n个结点,都看成独个连通分量,在所有边中选取权值最小的边,将两个顶点连成一个连通分量,舍弃两个顶点间的其他连线,重复此步骤,直到所有顶点都在一个连通分量上面为止。

​2、设计数据结构​

算法实现要引入以下数据结构:

(1)结构体数组Edge:存储边的信息,包括边的两个顶点信息和权值。

//辅助数组Edges的定义
typedef struct
{
VerTexType Head;//边的始点
VerTexType Tail;//边的终点
ArcType lowcost;//边上的权值
}Edge[arcnum];

(2)、Vexset[i]:标识各个顶点所属的连通分量。对每个顶点vi属于V,在辅助数组中存在一个相应元素Vexset[i]表示该顶点所在的连通分量。初始化时 Vexset[i] = i,表示各顶点自成一个连通分量。

//辅助数组Vexset的定义
int Vexset[MVNum];

​3、Kruskal算法描述​

void MiniSpanTree_Kruskal(AMGraph G)
{ //无向网G以邻接矩阵形式存储,构造G的最小生成树T,输出T的各条边
Sort(Edge);//将数组Edge中的元素按权值从小到大排序
for (i = 0;i < G.vexnum;++i)//辅助数组,表示各顶点自成一个连通分量
Vexset[i] = i;
for (i = 1;i < G.arcnum;++i)
{ //依次查看排好序的数组Edge中的边是否在同一连通分量上
v1 = LocateVex(G, Edge[i].Head);//v1为边的始点Head的下标
v2 = LocateVex(G, Edge[i].Tail);//v2为边的终点Tail的下标
vs1 = Vexset[v1];//获取边Edge[i]的始点所在的连通分量vs1
vs2 = Vexset[v2];//获取边Edge[i]的终点所在的连通分量vs2
if (vs1 != vs2)//边的两个顶点分属不同的连通分量
{
cout << Edge[i].Head << Edge[i].End;//输出此边
for (i = 0;i < G.vexnum;++i)//合并vs1和vs2两个分量,即两个集合统一编号
if (Vexset[i] == vs2)
Vexset[i] = vs1;//集合编号为vs2的都改为vs1
}
}
}

【最小生成树问题(Minimum Spanning Tree)——图】_数据结构_04

​练习:利用Prim算法、Kruskal算法构造最小生成树​

【最小生成树问题(Minimum Spanning Tree)——图】_最小生成树_05

【最小生成树问题(Minimum Spanning Tree)——图】_数据结构_06

【最小生成树问题(Minimum Spanning Tree)——图】_sed_07

​答案:​

【最小生成树问题(Minimum Spanning Tree)——图】_数据结构_08



举报

相关推荐

0 条评论