算法 - 并查集

并查集 - 知乎专栏

POJ 1611(并查集+知识)

POJ 2524(并查集)

这个时候我们就希望重新设计一种数据结构,能够高效的处理这三种操作,分别是

  • MAKE-SET(x),创建一个只有元素x的集合,且x不应出现在其他的集合中
  • UNION(x, y),将元素x所在集合Sx和元素y所在的集合Sy合并,这里我们假定Sx不等于Sy
  • FIND-SET(x),查找元素x所在集合的代表

并查集的具体实现。

对于有根树或者说森林的表示,可以用一个数组parent来实现。

parent[i]记录元素i的父节点的编号,如果节点i本身就是根节点,那么parent[i]就是-1。

MAKE-SET操作就是将parent数组赋值为-1,代码比较简单就不贴了。

UNION操作需要找到两个元素的根,并把其中一个元素的根节点设置为另一个元素的根节点,其实就是调用了两次FIND-SET,代码如下

下面讲一讲并查集的具体实现。对于有根树或者说森林的表示,可以用一个数组parent来实现。parent[i]记录元素i的父节点的编号,如果节点i本身就是根节点,那么parent[i]就是-1。MAKE-SET操作就是将parent数组赋值为-1,代码比较简单就不贴了。UNION操作需要找到两个元素的根,并把其中一个元素的根节点设置为另一个元素的根节点,其实就是调用了两次FIND-SET,代码如下

再来说下FIND-SET这个操作的实现。首先有个很朴素的算法,对父节点递归调用FIND-SET,直到找到根为止。如果父节点是-1,那么返回当前节点,否则递归调用查询父亲的根节点。代码如下

细心的同学会发现,如果我们这棵树很深,那么每次调用FIND-SET可能会需要O(n)的时间,总的复杂度在最坏情况下就是O(nQ)了,那不是更差了吗?

这时候,我们就要引入路径压缩这个概念。什么是路径压缩呢?就是在递归找到根节点的时候,把当前节点到根节点间所有节点的父节点都设置为根节点。举个例子

我们来看下图,首先我们有个这样的一棵树,现在要找到元素9所在树的根节点,在找根节点的过程中使用路径压缩,也就是说9到根的路径上的节点9,6,3,1的父节点都设置成为根节点0,所以呢,在FIND-SET(9)之后,树的形态就变成了下面的样子

我们可以看到经过路径压缩的节点及其子树到根节点的深度减小了很多,所以在以后的操作中,查找根节点的速度会快很多,平摊下来每次FIND-SET的操作几乎是常数级别的。代码也相当简单,就只多了一句话

刚刚在合并的过程中,并没有提到说到如何选取根,一般情况下两个根节点随意选取一个,如果需要更优的算法,可以按秩合并。除此以外,并查集还能处理带权的情况,因为时间的关系就不在线上分享了。

原文地址:https://www.cnblogs.com/qlky/p/8400723.html

时间: 2024-10-29 10:38:14

算法 - 并查集的相关文章

模板——最小生成树kruskal算法+并查集数据结构

并查集:找祖先并更新,注意路径压缩,不然会时间复杂度巨大导致出错/超时 合并:(我的祖先是的你的祖先的父亲) 找父亲:(初始化祖先是自己的,自己就是祖先) 查询:(我们是不是同一祖先) 路径压缩:(每个点只保存祖先,不保存父亲) 最小生成树kruskal:贪心算法+并查集数据结构,根据边的多少决定时间复杂度,适合于稀疏图 核心思想贪心,找到最小权值的边,判断此边连接的两个顶点是否已连接,若没连接则连接,总权值+=此边权值,已连接就舍弃继续向下寻找: 并查集数据结构程序: #include<ios

克鲁斯卡尔算法+并查集

算法要点:Kruskal算法的最难点在于怎样判断加入边(x,y)后是否形成了环. 问题可化为:判断边(x,y)的两个顶点x,y在图(实际是森林)mst中最否已经连通.如果已经连通,加入边将形成环:否则,不形成环. 在kruskal算法中,要用到并查集的合并和查找 并查集: 1 int getfa(int k) //找到最祖先 2 { 3 if(fa[k]==k) return k; 4 fa[k]=getfa(fa[k]); 5 return fa[k]; 6 } 7 8 void merge(

[经典算法]并查集

概述: 并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等. 使用并查集时,首先会存在一组不相交的动态集合 S={S1,S2,?,Sk},一般都会使用一个整数表示集合中的一个元素. 每个集合可能包含一个或多个元素,并选出集合中的某个元素作为代表.每个集合中具体包含了哪些元素是不关心的,具体选择哪个

最小生成树Kruskal算法+并查集实现

今天刚掌握Kruskal算法,写下随笔. 对于稀疏图来说,用Kruskal写最小生成树效率更好,加上并查集,可对其进行优化. Kruskal算法的步骤: 1.对所有边进行从小到大的排序. 2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入.那么加入的(u,v)一定是最佳的. 并查集: 我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点.而具体的连通方式无关紧要,好比集合中的元素没有先后顺序之分,只有"属于"与"不属于"的区别.

HDU 2586 How far away? Tarjan算法 并查集 LCA

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 23506    Accepted Submission(s): 9329 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every da

[SCOI2010][BZOJ1854] 游戏|二分图匹配|匈牙利算法|并查集

1854: [Scoi2010]游戏 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 3018  Solved: 1099[Submit][Status][Discuss] Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备最多只能使用一次. 游戏进行到最后,lxhgww遇到了终极boss

编程算法 - 并查集(disjoint set) 代码(C)

并查集(disjoint set) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 并查集(disjoint set)是一种常用的数据结构.树形结构, 包含查询(find)和合并(unite)操作. 时间复杂度O(a(n)), 比O(logn)要快. 代码: class DisjoinSet { static const int MAX_N = 10000; int par[MAX_N]; int rank[MAX_N]; public: void

Prim算法(并查集)

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小 基本思想:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合.算法从U={u0}(u0∈V).TE={}开始.重复执行下列操作: 在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止. 图例

*并查集的题*

POJ 1182 食物链http://acm.pku.edu.cn/JudgeOnline/problem?id=1182题目告诉有3种动物,互相吃与被吃,现在告诉你m句话,其中有真有假,叫你判断假的个数(如果前面没有与当前话冲突的,即认为其为真话)这题有几种做法,我以前的做法是每个集合(或者称为子树,说集合的编号相当于子树的根结点,一个概念)中的元素都各自分为A, B, C三类,在合并时更改根结点的种类,其他点相应更改偏移量.但这种方法公式很难推,特别是偏移量很容易计算错误.下面来介绍一种通用