-----------------siwuxie095
并查集基础
这里介绍并查集(Union Find),它是一种很不一样的树形结构
并查集的作用:可以非常高效地回答连接问题
「连接问题,即 Connection Problem」
首先简单的采用可视化的方式来看连接问题,如下:
在上图中有若干个点,点与点之间有可能相连
有这么一个问题:某两个点是不是连接在一起
如果这两个点比较近,通过这张图就可以非常容易看出来,它们是否
连接在一起
如果这两个点比较远,比如:左上角的点和右下角的点,只是通过人
眼来看这张图,就很难非常快速地回答这个问题
那么在这种情况下,就需要使用计算机,可是计算机如何高效地回答
这个问题呢?并查集这种数据结构就提供了一个非常好的解决方案
可能有人会问,在这样一张图判断两个点是否连接在一起,意义何在?
事实上,这只是一个抽象的问题的模型表述。连接问题在实际应用中
有着非常重要的作用
连接问题
连接问题最主要的一个作用就是可以判断网络中节点间的连接状态
注意:这里的 网络 是一个非常抽象的概念,并不一定就具体的代表
计算机网络
如:一个大型的社交网络,也是网络,即 用户之间形成的网络
在 FaceBook 中,用户和用户之间形成了好友的关系。那么这个好友的关系,
就是一种连接的关系,FaceBook 有很多用户,他们之间通过这种好友关系
的连接,就形成了一个巨大的网络
那么在 FaceBook 中就能问这样一个问题:任意的两个人A 和 B,他们之间
是否能够通过好友直接互相认识?这就是一个典型的连接问题
当然,网络不仅仅包含用户之间形成的社交网络,一个巨大的数据库中
有很多的音乐、电影、书籍 … 这些多媒体之间也可以形成网络
更不用提互联网的网页之间,本身形成的也是网络,路由器和路由器之间
的数据交换形成的也是网络,另外还有 道路交通、航班调度 … 这些全部
可以形成网络
那么在这些网络中,都可以使用并查集来回答类似的连接问题
集合
并查集还有另外一个非常重要的作用,就是可以实现数学中的集合
「数学中的集合类实现」
通过并查集的名字,大概也能看出来,并查集的并,其实就是实现
一个并集的意思
所以,在使用数学中集合的思路解决问题时,如果经常使用并集操
作,同时需要查询元素在集合中的状态的话,并查集也是一个非常
好的选择
路径问题
可能有人看到了连接问题,就会想到一个相应的问题,叫做 路径问题
熟悉算法的人都会知道,经常会有这样的问题:这些节点之间既然已经
能够连接了,那么能不能从一个节点通过一条路径到达另外一个节点?
路径问题,在图论中会有所涉及
连接问题与路径问题
连接问题和路径问题的区别:连接问题 比 路径问题 回答的问题要少
不难想象,路径问题直接求出了两个节点之间连接的路径,具体是什
么,而连接问题只需要回答两个节点之间是否相连就够了
正因为连接问题回答的问题比路径问题少,所以能设计出更快的算法
来解决连接问题,而不求出两个节点之间具体的路径
事实上,在计算机算法领域,经常会遇到类似的问题,即 并不需要回答
那么多问题,也正因为如此,可以设计出更高效的算法
如下:
(1)和 二分查找 作比较
现在要在一个有序数组中查找一个元素,可以使用二分查找法,也可以
使用顺序查找法
其中,二分查找法是 O(lgn) 的复杂度,顺序查找法是 O(n) 的复杂度
为什么顺序查找法会更慢一些呢?
一个更重要的原因就是:顺序查找法不仅查找到了这个元素,顺序查找
法还找到了这个元素在整个数组中的排名。也就是说,顺序查找法顺便
回答了 rank 这个问题
不仅如此,顺序查找法还把查找到的这个元素之前的所有元素的排名,
全部顺道求了出来,只不过我们不关心,所以没有存储,但是这个过
程都遍历了一遍。所以,二分查找法更高效
(2)和 select 作比较
现在要在任意一个数组中找出排名第 n 的元素,可以使用快速排序的
partition 的思路,设计出一个 select 算法。不过对于这种问题,一
个更直观的想法是:可以直接把这个无序数组排一遍序,当排好序之
后,就能直接找到排名为 n 的元素
其中,使用快排的思路实现的 select,是 O(n) 级别的复杂度,而先
排序再求出排名第 n 的元素,是 O(n*lgn) 的复杂度
为什么排一遍序会更慢一些呢?
一个更重要的原因,就是排好序以后,不仅能够回答排名第 n 的元素
是多少,还能回答排名是 n-1 的元素是多少,n-2 的元素是多少 …
事实上,可以回答排名第 x 的元素是多少(0 <= x <= n)
也就是说,排好序之后,能够回答的问题更多。而使用快排的思路实
现的 select,能够回答的问题相对少一些,也正因为如此,这个算法
更高效
(3)和 堆 作比较
用堆这种数据结构,可以非常快速的找出数据中的最大值和最小值
堆这种数据结构之所以高效,也是因为在问题模型中,我们只关心
最大值 和 最小值,不关心第二名、第三名 …
正是应用了这样的性质,设计出了堆这种高效的数据结构
所以,当针对一个某问题设计出一个算法后,不妨问问自己,我们
所实现的这个算法除了回答了问题本身之外,是不是额外回答了一
些别的问题
如果我们的算法额外回答了一些别的问题,那么很有可能存在一个
更高效的算法。它之所以高效,正是因为它没有回答额外问题
【made by siwuxie095】