前言
前面对 图的存储 和 **图的遍历(广度优先/深度优先)**做了简单的学习和了解,本篇文章,学习一下最小生成树的问题,以及对应解决这个问题的两种算法 普里姆算法 和 克鲁斯卡尔算法
1.最小生成树问题
首先看下面一道面试题:
假设目前有
N个顶点,每个顶点连接的路径不一样,设计一个算法,快速查找出能覆盖所有顶点的路径。
其实这个问题并不是求解两点之间的最短路径,而是设计一个路线,能覆盖所有的顶点。
如下连通图:
那么覆盖所有顶点的路径有一下几种
-
方法一
V0 ——> V5 ——> V4 --> V3 --> V2 -->V1 -->V8 --> V6 -->V7权重:11 + 26 + 20 + 22 + 18 + 21 + 24 + 19 = 161
-
方案二
V2 ——> V8 ——> V1 --> V0 --> V5 -->V6 -->V7 --> V3 -->V4权重:8 + 12 + 10 + 11 + 17 + 19 + 16 + 7 = 100 -
方案三
权重:8 + 12 + 10 + 11 + 16 + 19 + 16 + 7 = 99
由此可以看出,方法三是最优的方案,这就是最小生成树。
最小生成树:把构成连通网的最小代价的生成树称之为最小生成树。即假设有N个顶点,用N-1条边,连接所有顶点,而且权重的和最小的路径。
2.最小生成树求解(普里姆(Prim)算法)
普里姆算法思路:
1. 定义两个数组,adjvew 用来保存相关顶点下标,lowcost 保存顶点之间的权值。
2. 初始化两个数组,将与 V0 相关的 V1-V8 的所有顶点的权值赋值给 lowcost
adjvew[1-8],都赋值为 0,表示都是与 V0 相关的顶点(后面循环修改)
3. 循环 lowcost 数组,根据权值,找到权值最新的顶点的下标 k
4. 更新 lowcost 数组
5. 循环所有顶点,找到与下标为 k 的顶点,有关系的顶点,并更新 lowcost 数组和 adjvew 数组
注意:
更新 lowcost 数组的条件
1. 与下标为 k 的顶点之间有连接
2. 当前下标为 j 的顶点是否加入最小生成树
3. 下标为 k 的顶点与下标为 j 的顶点的权值比较,小于,则更新,
简单说就是要比较之前存储的权值,小于,则更新。
接下来,我们详细的解析一下上面的思路:
- 初始化
lowcost和adjvew
lowcost 数组(将与 V0 相关的 V1-V8 的所有顶点的权值赋值给 lowcost)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 0 | 10 | ∞ | ∞ | ∞ | 11 | ∞ | ∞ | ∞ |
默认将V0加入到最小生成树中,lowcost[0] = 0,10 和 11 表示顶点V0 连接顶点V1和V5的权值。
adjvew 数组(都赋值为 0,表示都是与 V0 相关的顶点)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
然后开始循环(从
i = 1开始,默认第一个已经加入最小树)
-
第一次循环
i = 1由上面的表格看出,
10是最小的权值,此时,
k = 1,在lowcost数组中10最小。且满足更新lowcost数组的三个条件,所以,
lowcost[1] = 0, 表示V1已经加入最小生成树,并打印信息lowcost数组0 1 2 3 4 5 6 7 8 0 0 ∞ ∞ ∞ 11 ∞ ∞ ∞ 然后,循环所有顶点,找到下标为
k顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V2 未加入最小生成树,且权值 18 < ∞,lowcost【2】= 18,adjvew【2】= 1 V8 未加入最小生成树,且权值 12 < ∞,lowcost【8】= 12,adjvew【8】= 1 V6 未加入最小生成树,且权值 16 < ∞,lowcost【6】= 16,adjvew【6】= 1lowcost数组0 1 2 3 4 5 6 7 8 0 0 18 ∞ ∞ 11 16 ∞ 12 adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 1 0 0 0 1 0 1 18,16,12对应V2、V6、V8,是针对V1相关的顶点,1是因为k = 1,表示与V1相连的顶点是V2、V6、V8 -
第二次循环
i = 2从
lowcost中找到 权值最小11的j = 5,便是k = 5所以,
lowcost[5] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 5顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V6 未加入最小生成树,且权值 17 > 16,不更新 V4 未加入最小生成树,且权值 26 < ∞,lowcost【4】= 26,adjvew【4】= 5 V3 未加入最小生成树,且权值 ∞ < ∞,不更新此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 18 ∞ 26 0 16 ∞ 12 adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 1 0 5 0 1 0 1 1,表示与V1相连的顶点V2、V6、V8,5是因为k = 5,表示与V5相连的顶点是V4 -
第三次循环
i = 3从
lowcost中找到 权值最小12的j = 8,便是k = 8所以,
lowcost[8] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 8顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V2 未加入最小生成树,且权值 8 < 18,lowcost【2】= 8,adjvew【2】= 8 V3 未加入最小生成树,且权值 21 < ∞,lowcost【3】= 21,adjvew【3】= 8此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 8 21 26 0 16 ∞ 0 V0、V1、V5、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 0 1 1,表示与V1相连的顶点V6、V8,5,表示与V5相连的顶点是V48是因为k = 8,表示与V8相连的顶点是V2、V3 -
第四次循环
i = 4从
lowcost中找到 权值最小8的j = 2,便是k = 2所以,
lowcost[2] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 2顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V3 未加入最小生成树,且权值 22 > 21,不更新此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 0 21 26 0 16 ∞ 0 V0、V1、V5、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 0 1 1,表示与V1相连的顶点V6、V8,5,表示与V5相连的顶点是V48是因为k = 8,表示与V8相连的顶点是V2、V3 -
第五次循环
i = 5从
lowcost中找到 权值最小16的j = 6,便是k = 6所以,
lowcost[6] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 6顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V7 未加入最小生成树,且权值 19 < ∞,lowcost【7】= 19,adjvew【7】= 6 V3 未加入最小生成树,且权值 22 > 21,不更新此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 0 21 26 0 0 19 0 V0、V1、V2、V5、V6、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 6 1 1,表示与V1相连的顶点V6、V8,5,表示与V5相连的顶点是V48,表示与V8相连的顶点是V2、V36是因为k = 6,表示与V6相连的顶点是V7 -
第六次循环
i = 6从
lowcost中找到 权值最小19的j = 7,便是k = 7所以,
lowcost[7] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 7顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V4 未加入最小生成树,且权值 7 < 26,lowcost【4】= 7,adjvew【4】= 7 V3 未加入最小生成树,且权值 16 < 21,lowcost【3】= 16,adjvew【3】= 7此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 0 16 7 0 0 0 0 V0、V1、V2、V5、V6、V7、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1,表示与V1相连的顶点V6、V8,8,表示与V8相连的顶点是V26,表示与V6相连的顶点是V77是因为k = 7,表示与V7相连的顶点是V3,V4 -
第七次循环
i = 7从lowcost中找到 权值最小7的j = 4,便是k = 4所以,
lowcost[4] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 4顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V3 未加入最小生成树,且权值 20 > 16,不更新此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 0 16 0 0 0 0 0 V0、V1、V2、V4、V5、V6、V7、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1,表示与V1相连的顶点V6、V8,8,表示与V8相连的顶点是V26,表示与V6相连的顶点是V77是因为k = 7,表示与V7相连的顶点是V3,V4 -
第八次循环
i = 8从
lowcost中找到 权值最小16的j = 3,便是k = 3所以,
lowcost[3] = 0,加入到最小生成树中,并打印信息然后,循环所有顶点,找到下标为
k = 3顶点各边权值小于此前这些顶点未被加入生成树权值,然后更新lowcost数组和adjvew数组V3 未加入最小生成树,且权值 20 > 16,不更新此时,
lowcost数组0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 V0、V1、V2、V3、V4、V5、V6、V7、V8都已经加入最小生成树adjvew数组(都赋值为 0,表示都是与 V0 相关的顶点)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1,表示与V1相连的顶点V6、V8,8,表示与V8相连的顶点是V26,表示与V6相连的顶点是V77是因为k = 7,表示与V7相连的顶点是V3,V4
代码实现:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
typedef int Status;
typedef struct {
int arc[MAXVEX][MAXVEX];
int numVertexes;
int numEdges;
}MGraph;
/* Prim算法生成最小生成树 */
void MiniSpanTree_Prim(MGraph G)
{
int min, i, j, k = 0;
int sum = 0;
int adjvex[MAXVEX]; // 保存相关顶点下标
int lowcost[MAXVEX]; // 保存相关顶点的权重
// 初始化第一个权值为0 ,将V0加入生成最小树
lowcost[0] = 0;
// 初始化第一个顶点的下标为0
adjvex[0] = 0;
// 初始化
for (i = 1; i < G.numVertexes; i++) {
// 将v0顶点与之有边的权值存入数组
lowcost[i] = G.arc[0][i];
// 初始化默认都为v0的下标
adjvex[i] = 0;
}
// 循环除了下标为 0 以外的全部顶点,找到z权重最小且没有加入到s最小树中的顶点
for (i = 1; i < G.numVertexes; i++) {
min = INFINITY;
j = 1; k = 0;
while (j < G.numVertexes) {
if (lowcost[j] != 0 && lowcost[j] < min) {
// 让当前权值成为最小值,更新min
min = lowcost[j];
// 当前最小值的下标赋值给 k
k = j;
}
j++;
}
}
// 打印
printf("(V%d, V%d) = %d\n", adjvex[k], k, G.arc[adjvex[k]][k]);
sum += G.arc[adjvex[k]][k];
// 当前顶点的权值设置为0 ,标志为已经加入最小树
lowcost[k] = 0;
// 循环所有顶点,找到与顶点k 相连接的顶点
for (j = 1; j < G.numVertexes; j++) {
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[j]) {
// 将较小的权值存入lowcost相应位置
lowcost[j] = G.arc[k][j];
// 下标为k的顶点存入adjvex
adjvex[j] = k;
}
}
printf("sum = %d\n", sum);
}
2.最小生成树求解(克鲁斯卡尔算法)
克鲁斯卡尔算法思路:
1.将 邻接矩阵 转换为 边表数组()
2.对边表数组根据权重值按照从小到大的顺序排序
3.遍历所有的边,打印不闭环的边,通过 parent 数组找到边的连接信息,避免闭环
4.如果不存在闭环问题,则加入到最小生成树中,并修改 parent 数组
克鲁斯卡尔算法详细解析如下:
设计如下的边表数据结构:
/* 对边集数组Edge结构的定义 */
typedef struct
{
int begin; // 开始
int end; // 结束
int weight; // 权重
}Edge ;
那么对上面所说的图的边表,排序后如下:
| begin | end | weight |
|---|---|---|
| 4 | 7 | 7 |
| 2 | 8 | 8 |
| 0 | 1 | 10 |
| 0 | 5 | 11 |
| 1 | 8 | 12 |
| 3 | 7 | 16 |
| 1 | 6 | 16 |
| 5 | 6 | 17 |
| 1 | 2 | 18 |
| 6 | 7 | 19 |
| 3 | 4 | 20 |
| 3 | 8 | 21 |
| 2 | 3 | 22 |
| 3 | 6 | 24 |
| 4 | 5 | 26 |
初始化parent数组,给初始值为0,默顶点间认没有连接
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
然后开始遍历 排好序的边表:
定义n 和 m ,分别表示begin 和 end,如果 m = n,表示 begin 和 end连接,就会产生闭合的环.
-
第
0次,i = 0begin = 4,end = 7,n = 4,m = 7n != m,所以parent[4] = 7,然后打印计算权重
parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
1次,i = 1begin = 2,end = 8,n = 2,m = 8n != m,所以parent[2] = 8,然后打印计算权重
parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
2次,i = 2begin = 0,end = 1,n = 0,m = 1n != m,所以parent[0] = 1,然后打印计算权重
parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
3次,i = 3begin = 0,end = 5,n = 0,m = 5然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[0] = 1,所以n = 1,parent[5] = 0,直接返回5n != m,所以parent[1] = 5,然后打印计算权重parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
4次,i = 4begin = 1,end = 8,n = 1,m = 8然后从
parent 数组中,可以找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[1] = 5,所以n = 5,parent[8] = 0,直接返回8n != m,所以parent[5] = 8,然后打印计算权重
parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 0 | 7 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
5次,i = 5begin = 3,end = 7,n = 3,m = 7然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[3] = 0,所以n = 3,parent[7] = 0,直接返回7n != m,所以parent[3] = 7,然后打印计算权重parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
6次,i = 6begin = 1,end = 6,n = 1,m = 6然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[1] = 5,parent[5] = 8,所以n = 8,parent[6] = 0,直接返回6n != m,所以parent[8] = 6,然后打印计算权重parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
7次,i = 7begin = 5,end = 6,n = 5,m = 6然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[5] = 8,parent[8] = 6,所以n = 6,parent[6] = 0,直接返回6n = m,所以不更新parent数组,所以不能加入最小树parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
8次,i = 8begin = 1,end = 2,n = 1,m = 2然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[1] = 5,parent[5] = 8,parent[8] = 6,所以n = 6,parent[2] = 8,parent[8] = 6,直接返回m = 6n = m,所以不更新parent数组,所以不能加入最小树parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
9次,i = 9begin = 6,end = 7,n = 6,m = 7然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[6] = 0,所以n = 6,parent[7] = 9,直接返回m = 6n != m,所以更新parent数组,所以parent[6] = 7,然后打印计算权重parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
10次,i = 10begin = 3,end = 4,n = 3,m = 4然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[3] = 7,parent[7] = 0,所以n = 7,parent[4] = 7,parent[7] = 0,直接返回m = 7n = m,所以不更新parent数组,所以不能加入最小树parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
11次,i = 11begin = 3,end = 8,n = 3,m = 8然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[3] = 7,parent[7] = 0,所以n = 7,parent[8] = 6,parent[6] = 7,直接返回m = 7n = m,所以不更新parent数组,所以不能加入最小树parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
12次,i = 12begin = 2,end = 3,n = 2,m = 3然后从
parent 数组中,找到当前顶点的尾部下标( 帮助我们判断2点之间是否存在闭环问题)parent[2] = 8,parent[8] = 6,parent[6] = 7,所以n = 7,parent[3] = 7,parent[7] = 0,直接返回m = 7n = m,所以不更新parent数组,所以不能加入最小树parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
13次,i = 13同上分析,
m = n,不更新 -
第
14次,i = 14同上分析,m = n,不更新parent数组如下:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
最终遍历完所有的边,找到最小树
代码实现:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535
typedef int Status;
// 图结构
typedef struct
{
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}MGraph;
// 边表结构
typedef struct {
int begin;
int end;
int weight;
}Edge;
/* 交换权值以及头和尾 */
void Swapn(Edge *edges,int i, int j)
{
int tempValue;
//交换edges[i].begin 和 edges[j].begin 的值
tempValue = edges[i].begin;
edges[i].begin = edges[j].begin;
edges[j].begin = tempValue;
//交换edges[i].end 和 edges[j].end 的值
tempValue = edges[i].end;
edges[i].end = edges[j].end;
edges[j].end = tempValue;
//交换edges[i].weight 和 edges[j].weight 的值
tempValue = edges[i].weight;
edges[i].weight = edges[j].weight;
edges[j].weight = tempValue;
}
/* 对权值进行排序 */
void sort(Edge edges[],MGraph *G)
{
//对权值进行排序(从小到大)
int i, j;
for ( i = 0; i < G->numEdges; i++)
{
for ( j = i + 1; j < G->numEdges; j++)
{
if (edges[i].weight > edges[j].weight)
{
Swapn(edges, i, j);
}
}
}
printf("边集数组根据权值排序之后的为:\n");
for (i = 0; i < G->numEdges; i++)
{
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
}
}
int Find(int *parent, int f)
{
while ( parent[f] > 0)
{
f = parent[f];
}
return f;
}
/* 生成最小生成树 */
void MiniSpanTree_Kruskal(MGraph G)
{
int i, j, n, m;
int sum = 0;
int k = 0;
int parent[MAXVEX];
Edge edges[MAXEDGE];
// 构建边表
for (i = 0; i < G.numVertexes - 1; i++) {
for (j = 1 + i; j < G.numVertexes; j++) {
if (G.arc[i][j] < INFINITYC) {
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = G.arc[i][j];
k++;
}
}
}
// 排序
sort(edges, &G);
// 初始化parent 数组为0. 9个顶点;
for (i = 0; i < MAXVEX; i++)
parent[i] = 0;
// 循环边表
for (i = 0; i < G.numEdges; i++) {
//获取begin,end 在parent 数组中的信息;
//如果n = m ,将begin 和 end 连接,就会产生闭合的环.
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].end);
// n与m不等,说明此边没有与现有的生成树形成环路
if (n != m) {
parent[n] = m;
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
sum += edges[i].weight;
}
}
printf("sum = %d\n",sum);
}