并查集基础

傻子都能看懂的并查集入门

Kruscal

图的存储不是用邻接矩阵,也不是用邻接表,而是直接存储所有边的结构体。

typedf struct edge{
  int x; //V1
  int y; //V2
  int w;//Weight
}

核心函数

int union_set(int x,int y, int w) //V1 V2 Weight
{
    x = find_set(x);    //x所在集合的代表
    y = find_set(y);    //y所在集合的代表
    if(x==y)        //**边的两顶点原本就在同一集合,若加入这条边则会成环,所在直接返回**
        return 0;
    father[x] = y;        //这是合并的关键一步,优化时可以用按秩合并,可以减小并查深度,但这样简单些
    sum+=w;
    return 1;
}

时间复杂度

O(|E|log|E|)

xmuoj 1500 最小生成树

#include <stdio.h>
#include <algorithm>
using namespace std;

typedef struct edge{
    int x;
    int y;
    int w;
}edge;

edge e[10002];
int father[102];
int Rank[102];
int length;

int find_father(int x)
{
    if (father[x] == x)
        return x;
    else
        return find_father(father[x]);
}

int compare(const void* a, const void* b){
    return (*(edge*)a).w - (*(edge*)b).w;
}

/*
尝试加入当前边。
如果该边两顶点所属集合不同,则把两个集合并成一个集合,应当加入;
否则使得被加入的集合产生环,不应当加入。
*/
int union_set(int x, int y, int w)
{
    x = find_father(x);
    y = find_father(y);
    if (x == y)
        return 0;
    if (Rank[x] < Rank[y])
        father[x] = y;
    else if (Rank[x] == Rank[y])
    {
        father[x] = y;
        Rank[y]++;
    }
    else
        father[y] = x;
    length += w;
    return 1;
}


// 如何找出加入最小生成树的边的源头都是第一个顶点?
int main()
{
    int N, i, j;
    while (scanf_s("%d", &N), N != 0)
    {
        //初始化
        for (i = 1; i <= N*(N - 1) / 2; i++)
            scanf_s("%d %d %d", &e[i].x, &e[i].y, &e[i].w);
        for (i = 1; i <= N; i++)
        {
            father[i] = i;
            Rank[i] = 1;
        }
        length = 0;

        //排序
        qsort(&e[1], N*(N - 1) / 2, sizeof(edge), compare);
        for (i = 1; i <= N*(N - 1) / 2; i++)
            union_set(e[i].x, e[i].y, e[i].w);
        printf("%d\n", length);
    }

    return 0;
}

/*
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
*/

Ocean
1.6k 声望74 粉丝

Mobaxterm


引用和评论

0 条评论