用破圈法求一个带权连通图的最小生成树

/**
*    实验题目:
*        用破圈法求一个带权连通图的最小生成树
*    实验目的:
*        深入掌握图的复杂操作、图遍历算法和最小生成树的概念,以及
*    最小生成树的构造算法。
*    实验内容:
*        编写一个程序,用破圈法求一个带权连通图的最小生成树。
*    破圈法思路:
*        任意取一个圈,去掉圈上图中权最大的边,反复执行这个
*    步骤,直到图中没有圈为止。
*
*/

#include <stdio.h>
#include <malloc.h>

#define INF     32767               //定义∞
#define MAXV    100                 //最大顶点个数

typedef char InfoType;
/*-------------------------以下定义邻接矩阵类型---------------------------*/
typedef struct
{
    int no;                         //顶点编号
    InfoType info;                  //顶点信息
}VertexType;                        //顶点类型

typedef struct
{
    int edges[MAXV][MAXV];          //邻接矩阵数组(用一个二维数组存放顶点间关系(边或弧)的数据)
    int n;                          //顶点数
    int e;                          //边数
    VertexType vexs[MAXV];          //存放顶点信息(用一个一维数组存放图中所有顶点数据)
}MatGraph;                          //完整的图邻接矩阵类型

//邻接表表示法-将每个顶点的邻接点串成一个单链表
/*-----------以下定义邻接表类型--------------*/
typedef struct ArcNode
{
    int adjvex;                     //该边的邻接点编号
    struct ArcNode *nextarc;        //指向下一条边的指针
    int weight;                     //该边的相关信息,如权值(用整型表示)
}ArcNode;                           //边结点类型

typedef struct VNode
{
    InfoType info;                  //顶点其他信息
    int cnt;                        //存放顶点入度,仅用于拓扑排序
    ArcNode *firstarc;              //指向第一条边
}VNode;                             //邻接表结点类型

typedef struct
{
    VNode adjlist[MAXV];            //邻接表头结点数组
    int n;                          //图中顶点数
    int e;                          //图中边数
}AdjGraph;                          //完整的图邻接表类型

/*-------------------------邻接矩阵的基本运算算法---------------------------*/
/*------------由边数组A、顶点数n和边数e创建图的邻接矩阵g--------------------*/
void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e)
{
    int i, j;

    g.n = n;
    g.e = e;
    for(i = 0; i < g.n; i++)
        for(j = 0; j < g.n; j++)
            g.edges[i][j] = A[i][j];
}

/*------------输出邻接矩阵g--------------------*/
void DispMat(MatGraph g)
{
    int i, j;

    for(i = 0; i < g.n; i++)
    {
        for(j = 0; j < g.n; j++)
        {
            if(g.edges[i][j] != INF)
                printf("%4d", g.edges[i][j]);
            else
                printf("%4s", "∞");
        }
        printf("\n");
    }
}

/*-------------------------邻接表的基本运算算法---------------------------*/
/*-------------------由边数组A、顶点数n和边数e创建图的邻接表G--------------------*/
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
    int i, j;
    ArcNode *p;

    G = (AdjGraph *)malloc(sizeof(AdjGraph));
    for(i = 0; i < n; i++)                              //给邻接表中所有头结点的指针域置初值NULL
    {
        G->adjlist[i].firstarc = NULL;
    }

    for(i = 0; i < n; i++)                              //检查邻接矩阵中的每个元素
    {
        for(j = n - 1; j >= 0; j--)
        {
            if(A[i][j] != 0 && A[i][j] != INF)          //存在一条边
            {
                p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
                p->adjvex = j;                          //邻接点编号
                p->weight = A[i][j];                    //边的权重
                p->nextarc = G->adjlist[i].firstarc;    //采用头插法插入结点p
                G->adjlist[i].firstarc = p;
            }
        }
    }
    G->n = n;
    G->e = e;
}

/*-------------------输出邻接表G--------------------*/
void DispAdj(AdjGraph *G)
{
    ArcNode *p;

    for(int i = 0; i < G->n; i++)
    {
        p = G->adjlist[i].firstarc;
        printf("顶点%d: ", i);
        while(p != NULL)
        {
            printf("%3d[%d]->", p->adjvex, p->weight);  //邻接点编号[权重]
            p = p->nextarc;
        }
        printf("∧\n");
    }
}

/*-------------------销毁图的邻接表G--------------------*/
void DestroyAdj(AdjGraph *&G)
{
    ArcNode *pre, *p;

    for(int i = 0; i < G->n; i++)
    {
        pre = G->adjlist[i].firstarc;                   //pre指向第i个单链表的首结点
        if(pre != NULL)
        {
            p = pre->nextarc;
            while(p != NULL)                            //释放第i个单链表的所有边结点
            {
                free(pre);
                pre = p;
                p = p->nextarc;
            }
            free(pre);
        }
    }
    free(G);                                            //释放头结点数组
}

#include <stdbool.h>

#define MAXE 100                                    // 最多的边数

typedef struct {
    int u;                                          // 边的起始顶点
    int v;                                          // 边的终止顶点
    int w;                                          // 边的权值
}Edge;

int visited[MAXV];                                  // 全局变量

/*------------------采用邻接矩阵存储的图g中从顶点v出发进行深度优先遍历---------------*/
static void MDFS(MatGraph g, int v)
{
    int w;

    visited[v] = 1;                                 // 置访问标记
    for(w = 0; w < g.n; w++)                        // 找顶点v的所有邻接点
    {
        if((g.edges[v][w] != 0) && (g.edges[v][w] != INF) && (visited[w] == 0))
        {
            MDFS(g, w);                             // 找顶点v的未访问过的邻接点w
        }
    }
}

/*-----------------判定图g的连通性-------------------*/
static bool connect(MatGraph g)
{
    bool flag = true;
    int k;

    for(k = 0; k < g.n; k++)
    {
        visited[k] = 0;
    }

    MDFS(g, 0);
    for(k = 0; k < g.n; k++)
    {
        if(visited[k] == 0)
        {
            flag = false;
        }
    }

    return flag;
}

/*------------------求图g的最小生成树---------------------*/
static void spantree(MatGraph &g)
{
    int i, j, k = 0, e;
    Edge temp;
    Edge edges[MAXE];

    for(i = 0; i < g.n; i++)                        // 获取图中所有边信息
    {
        for(j = 0; j < i; j++)
        {
            if((g.edges[i][j] != 0) && (g.edges[i][j] != INF))
            {
                edges[k].u = i;
                edges[k].v = j;
                edges[k].w = g.edges[i][j];
                k++;
            }
        }
    }

    for(i = 1; i < g.e; i++)                        // 将edges数组按w递减排序
    {
        if(edges[i].w > edges[i - 1].w)
        {
            temp = edges[i];
            j = i - 1;
            do
            {
                edges[j + 1] = edges[j];
                j--;
            }while((j >= 0) && (edges[j].w < temp.w));
            edges[j + 1] = temp;
        }
    }

    k = 0;
    e = g.e;
    while(e >= g.n)
    {
        g.edges[edges[k].u][edges[k].v] = INF;      // 删除第k条边
        g.edges[edges[k].v][edges[k].u] = INF;
        if(connect(g))                              // 若连通,则删除
        {
            e--;
            printf("  (%d)删除边(%d,%d):%d\n", g.e - e, edges[k].u, edges[k].v, edges[k].w);
        }
        else                                        // 若不连通,则恢复第k条边
        {
            g.edges[edges[k].u][edges[k].v] = edges[k].w;
            g.edges[edges[k].v][edges[k].u] = edges[k].w;
        }
        k++;
    }
    g.e -= e;                                       // 修改图中的边数
}


int main(void)
{
    MatGraph g;
    int A[MAXV][MAXV] = {
        {0, 28, INF, INF, INF, 10, INF}, {28, 0, 16, INF, INF, INF, 14},
        {INF, 16, 0, 12, INF, INF, INF}, {INF, INF, 12, 0, 22, INF, 18},
        {INF, INF, INF, 22, 0, 25, 24}, {10, INF, INF, INF, 25, 0, INF},
        {INF, 14, INF, 18, 24, INF, 0}
    };                                              // 图的邻接矩阵二维数组表示法
    int n = 7;                                      // 图顶点数
    int e = 9;                                      // 图边数

    CreateMat(g, A, n, e);                          // 建立图的邻接矩阵
    printf("图G的邻接矩阵:\n");
    DispMat(g);                                     // 输出邻接矩阵
    printf("产生最小生成树的过程:\n");
    spantree(g);
    printf("最小生成树如下:\n");
    DispMat(g);                                     // 输出邻接矩阵

    return 0;
}

测试结果:

图G的邻接矩阵:
   0  28  ∞  ∞  ∞  10  ∞
  28   0  16  ∞  ∞  ∞  14
  ∞  16   0  12  ∞  ∞  ∞
  ∞  ∞  12   0  22  ∞  18
  ∞  ∞  ∞  22   0  25  24
  10  ∞  ∞  ∞  25   0  ∞
  ∞  14  ∞  18  24  ∞   0
产生最小生成树的过程:
  (1)删除边(1,0):28
  (2)删除边(6,4):24
  (3)删除边(6,3):18
最小生成树如下:
   0  ∞  ∞  ∞  ∞  10  ∞
  ∞   0  16  ∞  ∞  ∞  14
  ∞  16   0  12  ∞  ∞  ∞
  ∞  ∞  12   0  22  ∞  ∞
  ∞  ∞  ∞  22   0  25  ∞
  10  ∞  ∞  ∞  25   0  ∞
  ∞  14  ∞  ∞  ∞  ∞   0

 

  • 10
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值