最小生成树之Kruskal算法

克鲁斯卡尔Kruskal算法简介

前面我们已经讲过一种求最小生成树的算法,Prim算法
Prim算法是对做操作,维护一个在最小生成树中的点的顶点集U,以及一个待处理点的顶点集V-U,每次找出连接这两个集合的最短边,构成最小生成树,并将顶点加入集合U,直到所有顶点都处理完毕。

Kruskal算法是对边做操作,每次选出一条最短边,如果它和当前最小生成树不构成回路就将其加入最小生成树,否则将其删除,直到所有边都处理完毕。

在这里插入图片描述
如果觉得上面的阐述过于抽象,那么请看下面这个例子。

例子引入

请添加图片描述
下面来实现Kruskal的具体操作:
1.找出最小权值的一条边,即B-E:12
2.继续找,即C-D:17
3.A-F:19
4.F-C:25
5.F-D:25,此时我们可以发现F-C-D三个顶点形成了回路,这种情况我们要舍去,即此时不进行任何“操作”
6.以此类推,直至边数为“顶点数-1”时结束

关键问题

在实现算法的过程中,我们要解决的问题有以下几个:
1.边的排序(从小到大)
2.如何判断有回路,即顶点有共同的祖先

对于问题1:我们只需根据边的权值对边进行排序,该排序算法的效率决定了整个Kruskal的效率,常见有冒泡排序、快速排序、堆排序等。

对于问题2:我们要找到两个顶点的祖先顶点,其实这就是并查集的内容,没接触过的可以观看视频并查集教程先理解一下并查集的思想

算法实现

#include <iostream>
#include <algorithm>
using namespace std;
#define MAX_VERTEX 10//最多顶点数
#define MAX_EDGE 100//最多边数

typedef struct EdgeType
{
    int from, to; //一条边上的两个顶点,from表示起点,to表示终点
    int w; //权值
}EdgeType;

typedef struct Gragh
{
    int vex[MAX_VERTEX]; //存放顶点
    EdgeType edge[MAX_EDGE]; //存放边
    int vexnum, arcnum; //顶点数、边数
}Gragh;

void CreatGragh(Gragh& G, int vexnum, int arcnum)
{
    G.vexnum = vexnum;
    G.arcnum = arcnum;
    for (int i = 0; i < vexnum; i++) //顶点赋值
    {
        int x;
        cin >> x;
        G.vex[i] = x;
    }
    int v1, v2, w;
    for (int i = 0; i < arcnum; i++) //边赋值
    {
        cin >> v1 >> v2 >> w;
        G.edge[i].from = v1;
        G.edge[i].to = v2;
        G.edge[i].w = w;
    }
}

void SortEdge_weigh(Gragh& G)
{
    for (int i = 0; i < G.arcnum - 1; i++)  //这里采用冒泡排序
    {
        for (int j = 0; j < G.arcnum - 1 - i; j++)
        {
            if (G.edge[j].w > G.edge[j + 1].w)
            {
                swap(G.edge[j], G.edge[j + 1]);
            }
        }
    }
}

int findRoot(int* parent, int v) //寻祖先顶点
{
    int t = v;
    while (parent[t] > -1) //初始化为-1
    {
        t = parent[t];
    }
    return t;
}

void outputMST(EdgeType x)
{
    printf("%d-%d:%d\n", x.from, x.to, x.w);
}

void Kruskal(Gragh& G, int* parent)
{
    for (int i = 0; i < G.vexnum; i++)
        parent[i] = -1;  //初始化
    int num=0, vex1, vex2;
    for (int i = 0; i < G.arcnum; i++)
    {
        vex1 = findRoot(parent, G.edge[i].from);
        vex2 = findRoot(parent, G.edge[i].to);
        if (vex1 != vex2) //如果没有共同祖先,说明可以构成连通
        {
            outputMST(G.edge[i]);
            parent[vex2] = vex1;
            num++;
        }
        if (num == G.vexnum - 1)  //但连通边达到了“顶点数-1”,最小生成树以形成,提前退出
            return;
    }
}

void print(Gragh G) {
    printf("   边     from   to   w\n");
    for (int i = 0; i < G.arcnum; i++) {
        printf("  Edge[%d]  %d      %d      %d\n", i, G.edge[i].from, G.edge[i].to, G.edge[i].w);
    }
}


int main()
{
    int vexnum, arcnum;
    printf("请输入顶点个数和边的个数:");
    cin >> vexnum >> arcnum;
    Gragh G;
    CreatGragh(G, vexnum, arcnum);
    SortEdge_weigh(G);
    printf("输出边储存的图:\n");
    print(G);
    int parent[100];
    printf("输出最小生成树:\n");
    Kruskal(G, parent);
    return 0;
}

运行结果
在这里插入图片描述

参考视频

【懒猫老师-数据结构-(44)最小生成树(Kruskal算法,克鲁斯卡尔算法)-哔哩哔哩】
【懒猫老师-数据结构-(45)最小生成树(Kruskal算法实现,克鲁斯卡尔算法)-哔哩哔哩】

  • 23
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值