0
点赞
收藏
分享

微信扫一扫

Kruskal算法

zhyuzh3d 2022-01-04 阅读 84

Kruskal算法

一:思想

​ 克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边 组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。

​ 判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。

假设遍历到一条由顶点 A 和 B 构成的边,而顶点 A 和顶点 B 标记不同,此时不仅需要将顶点 A 的标记更新为顶点 B 的标记,还需要更改所有和顶点 A 标记相同的顶点的标记,全部改为顶点 B 的标记。
在这里插入图片描述

例如,使用克鲁斯卡尔算法找上图的最小生成树的过程为:

首先,在初始状态下,对各顶点赋予不同的标记(用颜色区别),如下图所示:
在这里插入图片描述

对所有边按照权值的大小进行排序,按照从小到大的顺序进行判断,首先是(1,3),由于顶点 1 和顶点 3 标记不同,所以可以构成生成树的一部分,遍历所有顶点,将与顶点 3 标记相同的全部更改为顶点 1 的标记,如下图所示:

在这里插入图片描述

其次是(4,6)边,两顶点标记不同,所以可以构成生成树的一部分,更新所有顶点的标记为:

在这里插入图片描述

其次是(2,5)边,两顶点标记不同,可以构成生成树的一部分,更新所有顶点的标记为:

在这里插入图片描述

然后最小的是(3,6)边,两者标记不同,可以连接,遍历所有顶点,将与顶点 6 标记相同的所有顶点的标记更改为顶点 1 的标记:

在这里插入图片描述

继续选择权值最小的边,此时会发现,权值为 5 的边有 3 个,其中(1,4)和(3,4)各自两顶点的标记一样,如果连接会产生回路,所以舍去,而(2,3)标记不一样,可以选择,将所有与顶点 2 标记相同的顶点的标记全部改为同顶点 3 相同的标记:

在这里插入图片描述

当选取的边的数量相比与顶点的数量小 1 时,说明最小生成树已经生成。所以最终采用克鲁斯卡尔算法得到的最小生成树为上图所示。

二:实现代码

(一):结构

#define MAX_VERtEX_NUM 20       //定义最大顶点数量
#define VertexType int          //定义顶点数据类型
typedef struct edge{
    VertexType start;           //起始点
    VertexType end;             //指向点
    int weight;                 //起始点到终点上边的权值
}edge;
//定义辅助数组
typedef struct {
    VertexType point;       //顶点
    int sign;               //表示每个顶点所属的集合
}assist;

(二):算法

/**
 * 克鲁斯卡尔最小生成树
 * @param G
 */
void MinSpanTree_KRUSKAL(MGraph G){
    assist assists[MAX_VERtEX_NUM];
    edge edges[MAX_VERtEX_NUM];
    InitData(G,edges,assists);      //初始化数据
    //对辅助数组进行排序
    BubleSort(edges,G.arcnum);
    
    //用来记录最小生成树中边的数量
    int num = 0;

    //遍历所有的边
    for (int i = 0; i < G.arcnum; ++i) {
        //找到当前权值最小的起点和结束顶点在assits数组中的位置
        int start = LocateVex_ass(assists,edges[i].start,G.vexnum);
        int end = LocateVex_ass(assists,edges[i].end,G.vexnum);

        //如果start和end所表示的的点都存在
        if (start!=-1 && end!=-1){
            //开始的点与结束的顶点不属于同一个集合,则表明不会产生回路
            if (assists[start].sign != assists[end].sign){

                //输出,起点,终点,权值
                printf("%d\t%d\t%d\n",edges[i].start,edges[i].end,edges[i].weight);
                num++;

                //将新加入到最小生成树中的顶点的标记全部改为相同的
                for (int j = 0; j < G.vexnum; ++j) {
                    if (assists[j].sign == assists[end].sign ){
                        assists[j].sign = assists[start].sign;
                    }
                }
                //如果最小生成树中的边的数量比顶点数小1,则最小生成树已经生成
                if (num == G.vexnum-1){
                    break;
                }
            }
        }
    }
}

//初始化辅助数组
void InitData(MGraph G,edge* edges,assist* assists){
    //初始化辅助数组assits
    for (int i = 0; i < G.vexnum; ++i) {
        assists[i].point = G.vexs[i];       //顶点数据值
        assists[i].sign = i;                //初始化时每一个顶点的标志(所属集合)都是不一样的
    }
    //初始化edge数组
    int count=0;
    for (int i = 0; i < G.vexnum; ++i) {
        for (int j = i+1; j < G.vexnum; ++j) {
            if (G.arcs[i][j].adj!=INFINITY){
                edges[count].start = G.vexs[i];
                edges[count].end = G.vexs[j];
                edges[count].weight = G.arcs[i][j].adj;
                count++;
            }
        }
    }
}

/**
 * 在assit数组中查找顶点v,返回其在assit中的下标
 * @param assists
 * @param e
 * @param length    //数组长度
 * @return
 */
int LocateVex_ass(assist* assists,VertexType v,int length){
    for (int i = 0; i < length; ++i) {
        if (assists[i].point == v){
            return i;
        }
    }
    return -1;
}


//排序
/**
 * 将edge数组中的数据按照权值大小,从小到大进行排序
 * @param edges
 * @param numsize   数组长度
 */
void BubleSort(edge* edges,int numsize){
    edge tump;
    for (int i = 0; i < numsize; ++i) {
        for (int j = numsize-1; j >i ; j--) {
            if (edges[j].weight<edges[j-1].weight){
                tump = edges[j];
                edges[j] = edges[j-1];
                edges[j-1] = tump;
            }
        }
    }
}
举报

相关推荐

0 条评论