最近在复习贪心算法,复习到了并查集这个数据结构,因此写一道用并查集来解决的算法题。
题目
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
Your algorithm should run in O(n) complexity.
Example:
1
2
3
4 > Input: [100, 4, 200, 1, 3, 2]
> Output: 4
> Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
>
分析
这道题让我们求出一个无序数组中最长的连续序列的长度,难度在于时间复杂度要求在O(n)内,用并查集可以达到这个要求。
首先来复习一下并查集这个数据结构。
并查集
并查集是一种树型数据结构,用于处理一些不相交集合的合并及查询问题,用它来判断元素所在集合所用时间接近常数级,Kruskal最小生成树算法就用到了并查集来实现。
假设有一个动态集合S={s1,s2,s3,…..sn},每个子集合通过一个代表来标识,代表就是这个子集合中的一个元素,即根元素。要判断两个元素是否属于同一个子集合,判断它们的根元素是否是同一个代表即可。
随着union操作,子集合的个数会越来越少,因此称之为动态集合。
并查集主要涉及3种基本操作:
makeset
:初始化,每个元素作为一个子集合,它的父节点是本身,集合树高度为1find(x)
:找出某个元素x
的根元素,所需时间与树的高度成正比。在优化的并查集中,经常在find(x)
过程中把根节点更新为x
的父节点,这样可以降低树的高度,在总体上提高find
和union
的效率,这个优化被称为路径压缩union(x,y)
:合并包含x
和y
的集合,一般是让较低的树的根指向较高的树的根。只有当两棵树高度相同时,合并后总高度才增加。
解法
在本题中,连续的序列可以视为一个集合,集合中的元素值是连续的,而我们要做的事情就是找到最大的集合大小。
使用并查集,我们可以在O(n)时间内完成任务,因为经过优化后的find
操作耗时接近常数级。
本题,我们可以遍历一次数组,假设当前遍历到的元素值为x
,查询x+1
和x-1
是否在数组中,如果在,就合并它们所在的子集合,最终找出最大子集合的大小。
通过使用hash map
,我们可以解决三个问题:
- 把在数组中操作的问题转成在并查集中操作,键值对作为数组元素与并查集元素之间的联系
- 并查集中不能有两个重复的元素,否则结果会偏大
- 查询
x+1
和x-1
是否在数组中的操作耗时O(1)
另外需要注意,在这个问题的并查集中使用级别来合并子集合并不适用,要换成根节点所具有的孩子数。
代码
1 |
|