算法复习(1)——并查集

  翻翻我做过的286道题,发现忘了好多不记得啥了  呵呵呵。。。。

  于是毅然决定老师让好好复习复习。。

  



  第一节---并查集

  1.what is 并查集??

  并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其  间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往  在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来  描述。

(百度百科。。。废话一堆)

  我的见解:

    顾名思义,并查集,(并/查集)这种算法就是为了合并查找的集合。

  (图片来自CYJB的博客)

  2.怎么 nong??

  两个操作:1.查;

       2.并。

  下面,就来分别介绍一下查与并。

  我们可以用一棵树来表示一个集合,根节点可代表这个集合。

  查找:在上图中,比如查找元素d,便顺着向上找,找到根节点,便也确定了d点所在集合。

  合并:合并两个集合S1,S2,即可将S1(或S2)的根节点接到另一个树上,作为另一棵树的的子节点。

  3.基本代码实现

  查找:查找x的根节点

int find(int x)
{
    if(fa[x]==x)    return x;
    int t=find(fa[x]);
    return t;
}

  合并:

void merge(int x,int y)
{
    x=find(x);//分别找根节点
    y=find(y);
    if(x==y)    return;
    fa[x]=y;//将x的父亲(各个父亲)指向y;
}

  4.优化

  我们发现这样一个一个点找,一个一个点指,实在是太慢。。

  于是便有了两个优化。

  1.启发式合并

    将节点少的树接在节点多的树下。

    why??好理解啦,再查时,走的节点不就少了。

  代码

    

void morge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(S[fx]>S[fy])
    {
        F[fy]=fx;
        S[fx]+=S[fy];
    }
    else
    {
        F[fx]=fy;
        S[fy]+=S[fx];
    }
}
//S数组存的是个数

  2.路径压缩

      查找时将这条路径上的所有节点的父节点都设为根节点,这样可以大大减少之后的查找次数。

int find(int p)
{
    if(F[p]!=p)
        F[p]=find(F[p]);
    return F[p];}

  

  来道例题

  题目来源  https://neooj.com:8082/oldoj/problem.php?id=1046

1046: 亲戚

时间限制: 1 Sec  内存限制: 64 MB
提交: 480  解决: 251
[提交][状态][讨论版]

题目描述

  或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。

输入

  输入由两部分组成。

  第一部分以N,M开始。N为问题涉及的人的个数,M表示已经知道M对亲戚关1<=N,M<=100000,接下来M行,每行有两个数ai,
bi,表示已知ai和bi是亲戚。这些人的编号为1,2,3,…,
N。接下来输入一个整数P(1<=P<=100000),表示有P次询问,接下来P行,每行为ci, di,表示询问ci和di是否为亲戚。

输出

  对于每个询问输出一行:若ci和di为亲戚,则输出“Yes”,否则输出“No”。

样例输入

10 7

2 4

5 7

1 3

8 9

1 2

5 6

2 3

3

3 4

7 10

8 9

样例输出

Yes

No

Yes

题目分析:本题为并查集裸题。每个人看成一个集合,如果A是B的亲戚,将A,B合并即可。

查找时找祖先,祖先是一个,YES,否则NO。

#include<stdio.h>
int F[100001],S[100001];
int find(int p)
{
    if(F[p]!=p)
    {
        F[p]=find(F[p]);
    }
    return F[p];
}
void morge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(S[fx]>S[fy])
    {
        F[fy]=fx;
        S[fx]+=S[fy];
    }
    else
    {
        F[fx]=fy;
        S[fy]+=S[fx];
    }
}
int main()
{
    int n, m, a, b, x, y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        F[i]=i;
        S[i]=1;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        morge(x,y);
    }
    int p;
    scanf("%d",&p);
    for(int i=1;i<=p;i++)
    {
        scanf("%d%d",&a,&b);
        if(find(a)==find(b))
        {
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
    }
}

    蒟蒻的第一篇博文,如有不懂的地方,或是错误,请指正,在评论中提出。

原文地址:https://www.cnblogs.com/0724-zcsblog/p/8410701.html

时间: 2024-08-23 11:11:43

算法复习(1)——并查集的相关文章

算法复习之并查集

并查集是一种以树结构建立的能够高效处理分组问题中合并,查找操作的数据结构 支持三种基本操作:建立,查询,合并 实现: 1 #include<iostream> 2 using namespace std; 3 const int maxn=10000; 4 int a[maxn],heigh[maxn],par[maxn]; 5 void init(int n) 6 { 7 for(int i=0;i<n;i++) 8 { 9 par[i]=i; 10 heigh[i]=1; 11 }

普林斯顿公开课 算法1-10:并查集-优化的快速合并方法

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法1-11:并查集的应用

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法1-7:并查集基本概念

本节讲的是并查集的基本概念. 算法的开发步骤 对问题进行数学建模 寻找一个能够解决问题的算法 运行算法检测速度和内存是否符合要求 如果达不到要求,找出原因 寻找一种方法来解决问题 循环步骤,直到满意为止 以上就是算法开发比较科学的方法.算法开发完成之后需要进行数学分析. 并查集问题 给定N个物体,可以提供两种操作,一种是合并操作,一种是查找操作.合并操作就是将两个节点进行连接,查找操作就是判断两个节点是否连接在一起. 应用中的物体类型 实际应用中,并查集算法可以支持各种各样的物体类型,比如: 图

普林斯顿公开课 算法1-8:并查集 快速查找

本节讲的是并查集的第一种实现方法,这种方法查找操作开销很小而合并操作开销比较大. 数据结构 假设有N个节点,那么该算法的数据结构就是一个包含N个整数的数组id[]. 判断操作 判断节点p和节点q是否相连就是判断id[p]和id[q]的值是否一致. 合并操作 合并节点p和节点q就是将id数组中所有的id[p]都修改为id[q]. 这样的话,每次合并都要遍历整个数组,修改多个值,因此开销比较大. 复杂度 合并一次的复杂度是N,如果需要合并N次,那么整个程序的复杂度就是N^2.这样的复杂度不适合应用于

普林斯顿公开课 算法1-9:并查集-快速合并

本节讲的是并查集的另外一种实现方法.这种方法的合并操作开销很小,但是查找操作开销很大. 数据结构 这种算法的数据结构和快速查找方法的数据结构是一样的,也是N个整数组成的数组. 数组中每个元素id[i]的含义是指i的上级是id[i]. 根节点 一个节点的根节点就是id[id[id[...id[i]....]]],一直循环直到数值不再变化为止.由于算法的特性,这种循环永远不会造成死循环. 查找操作 查找操作就是判断两个节点的根节点是否相同. 合并操作 合并节点p和节点q就是将p节点的根节点的父节点设

hdu-1863畅通工程 最小生成树克鲁斯卡尔算法kruskal(并查集实现)

畅通工程 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 16994    Accepted Submission(s): 7134 Problem Description 省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).经过调查评估,得到的统计表中列出

普林斯顿公开课 算法1-9:并查集-高速合并

本节讲的是并查集的第二种实现方法.这样的方法的合并操作开销非常小,可是查找操作开销非常大. 数据结构 这样的算法的数据结构和高速查找方法的数据结构是一样的,也是N个整数组成的数组. 数组中每一个元素id[i]的含义是指i的上级是id[i]. 根节点 一个节点的根节点就是id[id[id[...id[i]....]]],一直循环直到数值不再变化为止.因为算法的特性,这样的循环永远不会造成死循环. 查找操作 查找操作就是推断两个节点的根节点是否同样. 合并操作 合并节点p和节点q就是将p节点的根节点

POJ 2421 Constructing Roads (Kruskal算法+压缩路径并查集 )

Constructing Roads Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 19884   Accepted: 8315 Description There are N villages, which are numbered from 1 to N, and you should build some roads such that every two villages can connect to each

Kruskal算法(贪心+并查集=最小生成树)

http://www.51nod.com/ Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算法的过程: (1) 将全部边按照权值由小到大排序. (2) 按顺序(边权由小到大的顺序)考虑每条边,只要这条边和我们已经选择的边不构成圈,就保留这条边,否则放弃这条边. 算法 成功选择(n-1)条边后,形成一个棵最小生成树,当然如果算法无法选择出(n-1)条边,则说明原图不连通. 以下图为例: 边排