图的最小生成树Kruskal算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Murdock_C/article/details/49561049

克鲁斯卡尔(Kruskal)算法(只与边相关)

算法描述:克鲁斯卡尔算法需要对图的边进行访问,所以克鲁斯卡尔算法的时间复杂度只和边又关系,可以证明其时间复杂度为O(eloge)。
算法过程:
1.将图各边按照权值进行升序排序
2.将图遍历一次,找出权值最小的边,(条件:此次找出的边不能和已加入最小生成树集合的边构成环),若符合条件,则加入最小生成树的集合中。不符合条件则继续遍历图,寻找下一个最小权值的边。
3.递归重复步骤1,直到找出n-1条边为止(设图有n个结点,则最小生成树的边数应为n-1条),算法结束。得到的就是此图的最小生成树。

克鲁斯卡尔(Kruskal)算法因为只与边相关,则适合求稀疏图的最小生成树。而prime算法因为只与顶点有关,所以适合求稠密图的最小生成树。
具体算法如下:有用到并查集的算法思想,具体可以自己百度“并查集”了解

#include <iostream>
#include <algorithm>
using namespace std;

struct edge      //边结构,存储边的两个节点及边的长度
{
    int start_point;
    int end_point;
    int length;
};

const int N = 100;  //设置最多的节点数为100
edge Edge[N];
int pre[N];         //pre数组保存节点的根节点信息,初始化为本身

bool cmp(edge a, edge b)   //排序函数的比较因子,按照边的长度、节点序号的升序排列
{
    if (a.length == b.length)
    {
        if (a.start_point == b.start_point)
        {
            return a.end_point < b.end_point;
        }
        else
            return a.start_point < b.start_point;
    }
    else
        return a.length < b.length;
}

int find(int x)               //查找节点x的根节点,并进行路径压缩,具体细节可以百度“并查集”
{
    int r = x;
    while (pre[r] != r)
    {
        r = pre[r];
    }
    int i = x;
    int j;
    while (i != r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }

    return r;
}


int main()
{
    int n, m, i, j, k, v, w;
    int s, e, length;
    int sum = 0;

    cin >> n;                       //输入边的数目
    for (i=0; i<n; ++i)             //初始化,存储边的信息,节点号我们按照从1开始
    {
        cin >> s >> e >> length;
        Edge[i+1].start_point = s;
        Edge[i+1].end_point = e;
        Edge[i+1].length = length;
    }

    for (i=0; i<n; ++i)
    {
        pre[i+1] = i + 1;            //初始化节点根节点信息
    }

    sort(Edge+1,Edge+n+1,cmp);       //对边的信息按照长度升序排序

    for (i=0; i<n; ++i)               //遍历所有的边
    {
        k = Edge[i+1].start_point;
        v = Edge[i+1].end_point;
        k = find(k);                  
        v = find(v);                 

        if (k != v)                   ////查找这条边的两个节点是否位于同一个连通分支,若不是,则将该边加入进去,并将它们的根节点连通起来(pre[k] = v)   
        {
            sum += Edge[i+1].length;
            pre[k] = v;
            cout << Edge[i+1].start_point << "->" << Edge[i+1].end_point << Edge[i].length << endl;
        }
    }

    cout << sum << endl;
    system("pause");
    return 0;
}

如有错误,欢迎指出。

猜你喜欢

转载自blog.csdn.net/Murdock_C/article/details/49561049
今日推荐