一、需求分析
1.1 问题描述
破圈法,是区别于避圈法(Prim算法和Kruskal算法)的⼀种寻找最⼩⽣成树的算法,也就是MST的⼀种⽅法。破圈法是“见圈破圈”,即如果看到图中有⼀个圈,就将这个圈的边去掉⼀条,直⾄图中再⽆⼀圈为⽌。破圈法是⼀种贪⼼算法,思想⼤体如下:
1.找到图中的⼀个圈;
2.删除其中的权最⼤的边;
3.重复上述操作,直到图中已⽆圈。
先将图G的边按照权的递减顺序排列后,依次检验每条边,在保持连通的情况下,每次删除最大权边,直到所有边都被遍历到无法删除任何一边(或余下n-1条边为止)。
1.2 基本要求
采用破圈法求一个带权连通图的最小生成树,并用 下图进行测试。图的存储结构自定义,并给出算法执行结果截图,以及算法效率分析。
1.3 任务
深入掌握图的复杂操作,图遍历算法和最小生成树的概念,以及最 小生成树的构造算法。 内容:编写一个程序,采用破圈法求一个带权连通图的最小生成树,并用 下图进行测试。图的存储结构自定义,并给出算法执行结果截图,以及算法效率分析。
二、概要设计
2.1 数据结构
利用的是图的存储结构。
2.2 程序模块
第一个模块——主函数main()的功能是:调用各函数完成相应的功能。
第二个模块——scanf()的功能是:在屏幕上输入邻接矩阵。
第三个模块——MAX( )的功能是:找出最大权值。
第四个模块——printf( )的功能是:输出各个信息。
2.3算法变量
a[n][n]:带权图的邻接矩阵,a[i][j]=w或a[i][j]=0;
max:标记当前找到的准备删去的边的权值;
p:标记找到的要删去的权值所在的行号;
q:标记找到的要删去的权值所在的列好;
am:标记找到的最大元素(am是为了保护权值大但不能删的边),如果a[i][j]不能删除,则可以让a[p][q]=am,a[q][p]=am来还原刚才删去的边;
I,j:二维数组的行号和列号
sm:图的边数,每删除一个边,sm就减1,当sm=n-1时,结束
wt:最小生成树的权值和
三、详细设计
3.1 主要函数的调用关系
INPUT()
{
int i, j;
printf("输入图的带权邻接矩阵:\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
scanf("%d", &a[i][j]);
}
}
OUTPUT(int a[n][n])
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
printf("%5d", a[i][j]);
printf("\n");
}
}
MAX(int a1[n][n], int am1, int p1, int q1)
{
int i, j, ptm, qtm;
int max;
max = 0;
for (i = 0; i < n; i++)
{
for (j = i; j < n; j++)
if ((a1[i][j] > max) && (a1[i][j] <= am1) && ((i != p1) || (j != q1)))
{
max = a1[i][j];
ptm = i;
qtm = j;
}
}
am = max;
printf("max=%5d\t", am);
p = ptm;
q = qtm;
a[p][q] = 0;
a[q][p] = 0;
}
WSHALL(int array[n][n])
{
int i, j, k, m = 0;
int r[n][n], B[n][n];
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
r[i][j] = 0;
B[i][j] = array[i][j];
//分界线
if (array[i][j] >= 1)
B[i][j] = 1;
else
B[i][j] = 0;
//分界线
}
}
for (j = 0; j < n; j++)
{
for (i = 0; i < n; i++)
if (B[i][j] >= 1)
{
for (k = 0; k < n; k++)
{
if (B[k][i] >= 1)
{
B[k][j] = B[j][k] = 1;
}
}
}
}
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
if (!B[i][j])
{
return 0;
}
}
return 1;
}
3.2 各模块的算法
各模块的功能实现,主要代码见源代码。
源代码
#include<stdio.h>
#define n 7
int a[n][n];
int flag, am, p, q;
INPUT()
{
int i, j;
printf("输入图的带权邻接矩阵:\n");
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
scanf("%d", &a[i][j]);
}
}
OUTPUT(int a[n][n])
{
int i, j;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
printf("%5d", a[i][j]);
printf("\n");
}
}
MAX(int a1[n][n], int am1, int p1, int q1)
{
int i, j, ptm, qtm;
int max;
max = 0;
for (i = 0; i < n; i++)
{
for (j = i; j < n; j++)
if ((a1[i][j] > max) && (a1[i][j] <= am1) && ((i != p1) || (j != q1)))
{
max = a1[i][j];
ptm = i;
qtm = j;
}
}
am = max;
printf("max=%5d\t", am);
p = ptm;
q = qtm;
a[p][q] = 0;
a[q][p] = 0;
}
WSHALL(int array[n][n])
{
int i, j, k, m = 0;
int r[n][n], B[n][n];
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
r[i][j] = 0;
B[i][j] = array[i][j];
//分界线
if (array[i][j] >= 1)
B[i][j] = 1;
else
B[i][j] = 0;
//分界线
}
}
for (j = 0; j < n; j++)
{
for (i = 0; i < n; i++)
if (B[i][j] >= 1)
{
for (k = 0; k < n; k++)
{
if (B[k][i] >= 1)
{
B[k][j] = B[j][k] = 1;
}
}
}
}
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
if (!B[i][j])
{
return 0;
}
}
return 1;
}
int main()
{
int i, j, sm, wt = 0;
am = 10000, p = -1, q = -1, sm = 0;
INPUT();
for (i = 0; i < n; i++)
{
for (j = i; j < n; j++)
{
if (a[i][j] > 0)
sm = sm + 1;
}
}
printf("\nsm=%d\n", sm);
printf("输出图的带权邻接矩阵:\n");
OUTPUT(a);
printf("\n");
while (sm > n - 1)
{
MAX(a, am, p, q);
flag = WSHALL(a);//华沙尔算法判断是否连通
//printf("flag= %d",flag);
{
if (flag == 1)
{
sm = sm - 1;
//printf("flag= %d",flag);
}
else
{
a[p][q] = am;
a[q][p] = am;
}
}
}
for (i = 0; i < n; i++)
for (j = i; j < n; j++)
{
wt = wt + a[i][j];
}
printf("\n\n输出最小生成树的带权邻接矩阵:\n");
OUTPUT(a);
printf("最小生成树的树权是: %d\n", wt);
}