【算法】Kruskal算法

前言

这篇博客来记录Kruskal算法,我比较奇怪的是课本居然没给实现,那我就按自己的理解实现一遍好了。

简介

Kruskal是一种求最小生成树的算法,相比Prim算法的加点策略,Kruskal算法采用加边策略.。

算法步骤描述

假设\(N=(V,{E})\)为一连通图,将\(N\)中的边按从小到大顺序排列

  1. \(n\)个顶点看作\(n\)个集合
  2. 按权值由小到大选择边,所选边应满足两个顶点不在同一顶点集合内,将该边放到生成树边的集合中,同时将该边两个顶点所在顶点集合合并
  3. 重复2直至所有顶点均在同一顶点集合内。

算法流程图示

1.png

基本与课本一致比课本丑

算法实现

为了实现判断不同顶点是否在同一集合内,我们使用并查集作为辅助数据结构来使用,定义如下:

struct VertexSet
{
    int vertex;							// 表示当前结点
    VertexSet *parent = nullptr;

    VertexSet() = default;
    VertexSet(int i) : vertex(i) {}		// 构造函数
    void changeGroup(VertexSet *set)
    {									// 将this结点加入到集合set里
        if (this->parent == nullptr)
        {
            this->parent = set;				// this无父节点则直接将this接入set
        }
        else
        {
            auto temp = this;				// 有父节点则寻找顶层父节点,将该父节点并入set
            while (temp->parent != nullptr)
            {
                temp = temp->parent;
            }
            temp->parent = set;
        }
    }
    bool inGroup(VertexSet *set)
    {								// 看结点this与set是否在同一集合
        VertexSet *temp = this;
        // 寻找顶层父节点
        while (temp != nullptr && temp->parent != nullptr)
        {
            temp = temp->parent;
        }
        VertexSet *tempSet = set;
        while (tempSet != nullptr && tempSet->parent != nullptr)
        {
            tempSet = tempSet->parent;
        }
        //////////////////////////////////////////////////
        // 比较
        if (temp == tempSet)
        {
            return true;
        }
        return false;
    }
};

而Kruskal算法主体实现如下

void Tree::kruskal()
{
    std::sort(arcs.begin(), arcs.end(), [](Arc A, Arc B)
              { return A.weight < B.weight; });				// 使用std::sort为边集合排序
    VertexSet vertexSet[vexnum + 1];					   //  创建适当大小的并查集 
    for (size_t i = 1; i <= vexnum; i++)
    {
        vertexSet[i] = VertexSet(i);						// 初始化并查集
    }
    int now = 0, size = vexnum;								// size用于记录停止条件
    while (size != 1)
    {
        int begin = arcs[now].tail;
        int end = arcs[now].head;
        if (vertexSet[begin].inGroup(&vertexSet[end]))
        {
            now++;
            continue;
        }
        std::cout << "<" << begin << "," << end << ">" << std::endl;
        now++;
        vertexSet[begin].changeGroup(&vertexSet[end]);
        size--;
    }
}

使用了std::sort来进行快速排序,可以说是偷了点懒吧┑( ̄Д  ̄)┍

完整代码

#include <iostream>
#include <vector>
#include <algorithm>

struct Arc
{
    int tail;
    int head;
    int weight;
    Arc(int t, int h, int w) : tail(t), head(h), weight(w) {}
    Arc() = default;
};

struct Tree
{
    std::vector<Arc> arcs;
    int vexnum;
    int arcnum;

    void init();
    void kruskal();
};

struct VertexSet
{
    int vertex;
    VertexSet *parent = nullptr;

    VertexSet() = default;
    VertexSet(int i) : vertex(i) {}
    void changeGroup(VertexSet *set)
    {
        if (this->parent == nullptr)
        {
            this->parent = set;
        }
        else
        {
            auto temp = this;
            while (temp->parent != nullptr)
            {
                temp = temp->parent;
            }
            temp->parent = set;
        }
    }
    bool inGroup(VertexSet *set)
    {
        VertexSet *temp = this;
        while (temp != nullptr && temp->parent != nullptr)
        {
            temp = temp->parent;
        }
        VertexSet *tempSet = set;
        while (tempSet != nullptr && tempSet->parent != nullptr)
        {
            tempSet = tempSet->parent;
        }
        if (temp == tempSet)
        {
            return true;
        }
        return false;
    }
};

void Tree::init()
{
    std::cout << "请输入顶点个数与边个数:";
    std::cin >> vexnum >> arcnum;
    for (size_t i = 1; i <= arcnum; i++)
    {
        int begin, end, weight;
        std::cout << "请输入弧尾弧头与两点间距离:";
        std::cin >> begin >> end >> weight;
        arcs.emplace_back(std::move(Arc(begin, end, weight)));
    }
}

void Tree::kruskal()
{
    std::sort(arcs.begin(), arcs.end(), [](Arc A, Arc B)
              { return A.weight < B.weight; });
    VertexSet vertexSet[vexnum + 1];
    for (size_t i = 1; i <= vexnum; i++)
    {
        vertexSet[i] = VertexSet(i);
    }
    int now = 0, size = vexnum;
    while (size != 1)
    {
        int begin = arcs[now].tail;
        int end = arcs[now].head;
        int weight = arcs[now].weight;
        if (vertexSet[begin].inGroup(&vertexSet[end]))
        {
            now++;
            continue;
        }
        std::cout << "<" << begin << "," << end << ">" << std::endl;
        now++;
        vertexSet[begin].changeGroup(&vertexSet[end]);
        size--;
    }
}

int main()
{
    Tree aTree;
    aTree.init();
    aTree.kruskal();
}

图使用邻接矩阵存储。

posted @ 2022-06-02 15:02  帝皇の惊  阅读(39)  评论(0)    收藏  举报