CS61B - Lec 20 - DisjointSets


上面三章主要讲的是算法复杂度,这对于中国学生来说还是比较简单的,直接暴力数组求和,注意循环起止条件即可。不过还是要多加练习。从这章开始就是高阶的数据结构的讲解了。

Disjoint Sets

本章的思路是先提供一个ADT,然后用几种方法来实现它,比较它们性能的差异。

在这里插入图片描述
实现如图所示的两个功能:连接两个items p和q的方法connect(p, q),检查两个items是否相连的方法isConnected(p, q)。
Disjointed Sets(并查集) 的概念就是,将几个元素依次相连,则这几个元素彼此都可视为connected;如果将两个彼此相连的数据集分别挑两个元素相连,那么这两个数据集中所有元素connected。
可见是类似于集合的性质,不关心顺序,只关心元素是否在这个集合中。

Quick Find

这一步首先要实现tracking of sets。

首先想到的是将各个数据集进行编号,如果connect两元素,将所属的集合也进行合并,判断是否connected就看是否在一个集合中。为此,需要保存各个元素的值以及对应的集合,很容易想到,用一个Map<Integer(value), Integer(Set num)> 来实现。但是说这种类型效率也不高。如果用HashTable的话,isConnected很容易,只有O(1),connect倒是麻烦了点,需要变换所有同集合的集合编号,遍历整组数据,有O(n)的复杂度。可我觉得这种类型还不错啊,josh现在没说为什么不用这种类型。

他第一个给出的方法是这样:
在这里插入图片描述
建一个set的list。明显不行,复杂度很高。并且编代码也很复杂,isConnected好说,connect不好搞,复杂度如下:
在这里插入图片描述
改进的方法:
在这里插入图片描述建一个数组,编号代表value,值代表Set Number,很聪明
在这里插入图片描述
同样的,isConnected代码编写比较简单,而且速度快只有O(1),但connect比较慢,还是需要遍历整组数据,有O(n)的复杂度。
在这里插入图片描述
继续改进connect方法。

Quick Union

在这里插入图片描述
首先考虑这个问题,怎样才能使connect方法只用改变一个值?(现在需要改变整个集合的值)

我不知道你们想出来没,我反正是没想出来。
在这里插入图片描述
答案是将数组的值变为parent,当前元素连接的上一个元素。为什么没想到?因为这倒是改变了一个值,但是如果想connect(5, 2),能只将id[5]变为2么??当然不行,那样5和3的连接就丢了。得这么搞:
在这里插入图片描述
先找到5的root3,然后找到2的root0,最后将3和0连起来,这尼玛是改变一个值了,但还是很复杂啊。。。
复杂度的问题先搁置一边。为什么不直接将3和2连起来呢?因为当不断连接时,tree的高度会不断升高,找root的复杂度也会越来越高。
在这里插入图片描述
上图为QuickUnion的算法。这是一个让tree变得越来越胖的算法。
在这里插入图片描述
总结一下三种方法的复杂度。QuickUnion的复杂度其实是取决于tree的高度,如果能良好的控制高度,变成一个胖胖树,复杂度可以降到常数。
由上文已知为了让树变胖,需要将root相连,但是连接的顺序呢?

Weighted Quick Union

在这里插入图片描述
当然是把小树连在大树上了!
在这里插入图片描述
所以,现在需要一个反应树大小的值。有两种方式:第一种将代表root的负数设为多个值,显示各个树的大小差异;第二种另外建一组代表大小的数据。
在这里插入图片描述
考虑它的复杂度。上图是最大化树高度的例子,可见树高度最高为Θlog(N)。得到复杂度。
在这里插入图片描述
那么为什么不直接用height作为标准连接两个树?因为height不好获得,而且复杂度想一想,依然是O(log(N))。

Path Compression

还能不能做的更好呢!
在这里插入图片描述
当要connect(15, 10)的时候,将所有经过的元素的root都指向0。
在这里插入图片描述

发布了20 篇原创文章 · 获赞 0 · 访问量 170

猜你喜欢

转载自blog.csdn.net/fourier_transformer/article/details/105407785