并查集—分离集合森林实现

并查集总结

今天总结一下并查集,这个完了之后,寒假学的数据结构基础的模板类的题目差不多就完了,对于模板题,敲上10遍、20遍、30遍,那么模板就不是模板,就成为了你自己的东西,就好像 A+B 一辈子也忘不了,以后每天敲一遍模板题,加深对模板的理解。

并查集,一般使用的是 数组实现、树实现,其中数组实现时间复杂度较高,树实现也就是分离集合森林 查找、合并的时间复杂度不会

超过 O(log2n)

n个人,m对有亲戚关系的

10 7

1 2

2 3

2 4

3 4

5 6

6 7

8 9

初始化:{1}  {2}  {3}  {4} {5} {6} {7} {8}  {9}  {10}

初步合并 {1,2} {2,3} {2,4} {3,4} {5,6} {6,7} {8,9} {10}

最终合并 {1,2,3,4} {5,6,7} {8,9} {10}

分离集合森林如下:

一、树实现

并查集中,每一个集合代表一个分离集合树,多个集合形成一个森林,树根当做集合的代表,每一个节点都有一个父指针来表示附属关系,根节点指向自身,在一个树高度很低的树种查找一个节点所用的时间很少。所以 并查集的分离集合树的 开辟一个秩的int 数据来保证构造的分离集合树的高度较低,在合并的时候,让较小的秩的根指向较大秩的根,若俩个秩相等,任意一个指向另一个,并且它的秩+1;

POJ 2524

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
const int N= 50001;
using namespace std;
struct node{
    int data; // 节点数据域
    int zhi;  //秩 存放子树高度
    int parent;//父节点
}t[N];
int n,m;
int Find(int r)//查
{
    while(t[r].parent!=r)//判断双亲是不是自己
    {
        r = t[r].parent; //循环查找X,
    }
    /*
     if(t[r].parent==r)  //递归实现
        return r;
    else
        return (Find(t[r].parent));
    */
    int i=r,j;//压缩路径
    while (t[i].parent!=r)//i的父亲不是r,进行压缩
    {
        j=t[i].parent;//记录i的父亲
        t[i].parent=r;//改变i的父亲
        i=j;//判断i的父亲
    }
    return r;
}

void Merge(int x,int y)//并:合并x y的子树
{
    x = Find(x);  //找x所在子树的编号
    y = Find(y);
    if(t[x].zhi>t[y].zhi) //较小秩 指向 较大秩
        {
            t[y].parent = x; //y链接到x上,x为父节点
        }
    else
    {
        t[x].parent = y;
        if(t[x].zhi == t[y].zhi) //x连接y,y做为父节点
            t[y].zhi++; //y的子树高度+1
    }
}
void init()
{
    for(int i = 1;i<=n;i++)
        {
            t[i].data = i;
            t[i].zhi = 0;//初始化 树高度为0
            t[i].parent = i;//父节点是自己
        }
}
int main()
{
    int x,y,l=0;
    while(scanf("%d%d",&n,&m))
    {
        if(n==0 && m==0)
            break;
        l++;
        init();
        for(int i = 0;i<m;i++)
        {
            scanf("%d%d",&x,&y);
            Merge(x,y);
        }
        int sum = 0;
        for(int i = 1;i<=n;i++)
        {
            if(t[i].parent!=i)
                sum++;
        }
        printf("Case %d: %d\n",l,n-sum);
    }
    return 0;
}

   数组实现

#include <stdio.h>
int bin[100010];
int findx(int x)
{
    int r=x;
    while(bin[r] !=r)
        r=bin[r];
    return r;
}
void merge(int x,int y)
{
    int fx,fy;
    fx = findx(x);
    fy = findx(y);
    if(fx != fy)
        bin[fx]=fy;
}
int main()
{
    int n,m,i,x,y,count;
	int t=1;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0 && m == 0)
            break;
        for(i=1;i<=n;i++)
            {
                bin[i] = i;
            }
        for(i=1;i<=m;i++)
        {
            scanf("%d %d",&x,&y);
            merge(x,y);
        }
        for(count = 0, i=1;i<=n;i++)
            if(bin[i] == i)
                count ++;
        printf("Case %d: %d\n",t++,count);
    }
    return 0;
}

并查集—分离集合森林实现,布布扣,bubuko.com

时间: 2024-10-24 15:57:19

并查集—分离集合森林实现的相关文章

BC68(HD5606) 并查集+求集合元素

tree Accepts: 143 Submissions: 807 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 有一个树(nn个点, n-1n−1条边的联通图),点标号从11~nn,树的边权是00或11.求离每个点最近的点个数(包括自己). 输入描述 第一行一个数字TT,表示TT组数据. 对于每组数据,第一行是一个nn,表示点个数,接下来n-1n−1,每行三个整数u,

并查集(集合合并) + 缩点

正解是dfs的一道题目被我以为成了并查集,结果坑了队友.现在分析一下自己当时为什么会想成是并查集呢. 题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2588 题意:告诉一张n点m边的图,求哪些边是桥.其实就是缩点,然而却大脑短路,直接认定是并查集了,现在的我真的不能理解当时的自己.... 当时想的是把所有的环缩成一个点,然后剩下的边就是桥了,缩点,第一时间想的就是并查集,其实并查集的操作一般有三个: 1.初始化 把

并查集:不相交集合

并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.支持三种操作: Make-Set : 用于建立单元素集合. Find-Set:确定元素属于哪一个子集.它可以被用来确定两个元素是否属于同一子集. Union:将两个子集合并成同一个集合. 1.并查集的数组表示 //x表示元素,s[x]表示x所属集合 int s[N]; Make-Set(x){ s[x] = x; } Find-Set(x){ return s[x]; } //将y在所属集合

并查集 (Union-Find Sets)及其应用

定义 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并. 主要操作 初始化 把每个点所在集合初始化为其自身. 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N). 查找 查找元素所在的集合,即根节点. 合并 将两个元素所在的集合合并为一个集合. 通常来说,合并之前,应先判断两个元素是否属于

POJ 1611 The Suspects(特别无语的并查集)

很简单的一道题目,开始用的是并查集的分离集合森林做,不知道怎么的特别不稳定,太奇怪了,WA无数次,无奈之下改成一维数组了..sad AC #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <math.h> #define PI acos(-1,0) using namespa

数据结构--并查集的原理及实现

一,并查集的介绍 并查集(Union/Find)从名字可以看出,主要涉及两种基本操作:合并和查找.这说明,初始时并查集中的元素是不相交的,经过一系列的基本操作(Union),最终合并成一个大的集合. 而在某次合并之后,有一种合理的需求:某两个元素是否已经处在同一个集合中了?因此就需要Find操作. 并查集是一种 不相交集合 的数据结构,设有一个动态集合S={s1,s2,s3,.....sn},每个集合通过一个代表来标识,该代表中集合中的某个元素. 比如,若某个元素 x 是否在集合 s1 中(Fi

ZOJ4109 Welcome Party (并查集+优先队列)

首先提取题目信息,我们需要计算朋友的关系,以及计算不开心值. 其实看题目我们很容易就有并查集的感觉,之后我们要去验证这个算法是正确的 题目要求的是求不开心值,那么我们就可以想到使用并查集求集合总数就可以了 虽然朋友不存在传递性,但是这并不影响不开心值的大小,因为如果a和b是朋友,b和c是朋友 那么虽然a和c不一定是朋友,但是假如a进去了,那么我们可以走b再走c,这样不会增加不开心值. 因为我们有媒介,也就是并查集的传递性没有破坏. 但是题目有个要求是说字典序最小,其实这也是常见的做法,我怀疑很有

poj1988 Cube Stacking(并查集

题目地址:http://poj.org/problem?id=1988 题意:共n个数,p个操作.输入p.有两个操作M和C.M x y表示把x所在的栈放到y所在的栈上(比如M 2 6:[2 4]放到[1 6]上为[2 4 1 6]),C x为输出x下面有几个数. 思路:并查集每个集合以栈最下面的数为根,维护两个数组num[x]表示x所在集合节点总数,count[x]表示x下方节点个数.每次查找压缩路径的时候更新count(换父节点的时候每轮都把父节点的count加给儿子,就可以一直更新到x所在栈

HDU 1213 How Many Tables (并查集)

How Many Tables Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 22498 Accepted Submission(s): 11193 Problem Description Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner ti