数据结构与算法专题——第五题 并查集
2021-03-30 15:26
标签:alt 链表 初始 并查集 rgs hash函数 压缩 常量 union 有时候我们会遇到这样的场景,比如:M={1,4,6,8},N={2,4,5,7},我的需求就是判断{1,2}是否属于同一个集合,当然实现方法有很多,一般情况下,普通青年会做出O(MN)的复杂度,那么有没有更轻量级的复杂度呢?嘿嘿,并查集就是用来解决这个问题的。 从名字可以看出,并查集其实只有两种操作,并(Union)和查(Find),并查集是一种算法,所以我们要给它选择一个好的数据结构,通常我们用树来作为它的底层实现。 1. 节点定义 2. Union操作 原始方案 首先对集合的所有元素进行打散,这样的话每个元素都是一个独根的树,然后开始Union其中某两个元素,让他们成为一个集合,最坏情况下我们进行M次的Union时会存在这样的一个链表的场景,如下图: 从图中可以看到,Union时出现了最坏的情况,而且这种情况还是比较容易出现的,导致在Find的时候也就没啥意义了,复杂度为O(N)。 按秩合并 其实你可以发现出现这种情况的原因在于我们Union时都是将合并后的大树作为小树的孩子节点存在,那么我们在Union时能不能判断一下,将小树作为大树的孩子节点存在,最终也就降低了新树的深度,比如图中的Union(D,{E,F})的时候可以做出如下修改。 可以看出,图中右侧正确的做法有效的降低了树的深度,在N个元素的集合中,构建树的深度不会超过LogN层。M次操作的复杂度为O(MlogN),从代码上来说,我们用Rank来统计树的秩,可以理解为树的高度,独根树时Rank=0,当两棵树的Rank相同时,可以随意挑选合并,在新根中的Rank++就可以了。 3.Find操作 我们学算法,都希望能把一个问题优化到地球人都不能优化的地步,针对logN的级别,我们还能优化吗?当然可以。 路径压缩 在Union和Find这两种操作中,显然在Union操作上已经做到了极致,那我们可不可以在Find上面捣鼓一下,运用伸展树的玩法,它的伸展思想就是压缩路径。 从图中可以看出,当我 Find(F) 的时候,找到 “F” 后就要开始准备一直回溯,在回溯的过程中,把该节点的父亲指向根节点。最终我们会形成一个压缩后的树,当我们再次 Find(F) 的时候,只要 O(1) 的时间就可以获取,这里有个注意的地方就是Rank,当在路径压缩时,最后树的高度可能会降低,可能你会意识到原先的Rank就需要修改了,所以我要说的就是:当路径压缩时,Rank保存的就是树高度的上界,而不仅仅是明确的树高度,可以理解成"伸缩椅"伸时候的长度。 或许你还注意到了在路径压缩后,我们将LogN的复杂度降低到Alpha(N),Alpha(N)可以理解成一个比hash函数还有小的常量,嘿嘿,这就是算法的魅力。 最后上一下总的运行代码: class Program 数据结构与算法专题——第五题 并查集 标签:alt 链表 初始 并查集 rgs hash函数 压缩 常量 union 原文地址:https://blog.51cto.com/huangxincheng/2525497一:场景
二:操作
///
#region 合并两个不相交集合
///
#region 查找x所属的集合
///
{
static void Main(string[] args)
{
//定义 6 个节点
char[] c = new char[] { ‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘, ‘F‘ }; DisjointSet set = new DisjointSet();
set.Init(c);
set.Union(‘E‘, ‘F‘);
set.Union(‘C‘, ‘D‘);
set.Union(‘C‘, ‘E‘);
var b = set.IsSameSet(‘C‘, ‘E‘);
Console.WriteLine("C,E是否在同一个集合:{0}", b);
b = set.IsSameSet(‘A‘, ‘C‘);
Console.WriteLine("A,C是否在同一个集合:{0}", b);
Console.Read();
}
}
///
文章标题:数据结构与算法专题——第五题 并查集
文章链接:http://soscw.com/index.php/essay/70042.html