数据结构(用破圈法求一个带权连通图的最小生成树 )

一、需求分析

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);
     }

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,我会回答你的问题。 破圈法是一种带权连通最小生成树的经典算法之一,它的基本思想是:从连通中任选一条边作为生成树的第一条边,然后依次添加剩余的边,直到生成一棵最小生成树。 下面是用C++实现破圈法带权连通最小生成树的示例代码: ```c++ #include <iostream> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1005; // 最大顶点数 const int INF = 0x3f3f3f3f; // 无穷大 struct Edge { int u, v, w; Edge(int u_, int v_, int w_) : u(u_), v(v_), w(w_) {} }; int n, m; // 顶点数、边数 int fa[MAXN]; // 并查集数组 int find(int x) { // 并查集查找父节点 return (fa[x] == x) ? x : (fa[x] = find(fa[x])); } void merge(int x, int y) { // 并查集合并 fa[find(x)] = find(y); } bool cmp(Edge a, Edge b) { // 边长从小到大排序 return a.w < b.w; } int Kruskal(vector<Edge> &edges) { // 破圈法最小生成树 int ans = 0; sort(edges.begin(), edges.end(), cmp); // 边长从小到大排序 for (int i = 1; i <= n; i++) fa[i] = i; // 初始化并查集 for (int i = 0; i < m; i++) { int u = edges[i].u, v = edges[i].v, w = edges[i].w; if (find(u) != find(v)) { // 判断是否在同一连通块 merge(u, v); // 合并连通块 ans += w; // 加上边长 } } return ans; } int main() { cin >> n >> m; vector<Edge> edges; for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; edges.push_back(Edge(u, v, w)); } int ans = Kruskal(edges); cout << ans << endl; return 0; } ``` 上述代码中,首先定义了一个边的结构体Edge,其中包括边的起点、终点和边权重。然后,定义了并查集的find和merge操作及判断两个点是否在同一连通块的函数。接着,定义了一个比较函数cmp,用于将边按照权值从小到大排序。最后,实现了破圈法的Kruskal函数,其中对边进行排序,初始化并查集,依次添加边,并判断是否在同一连通块,最后返回最小生成树的边权和。在main函数中,读入顶点数、边数和边的信息,调用Kruskal函数最小生成树,并输出结果。 希望我的回答能够帮到你,如果你还有其他问题,可以继续问我哦。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-MTing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值