题目: 给定一个未排序的整数数组,找出最长连续序列的长度。 要求算法的时间复杂度为 O(n)。
输入: [100, 4, 200, 1, 3, 2] 输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。
方法1. Hash表
Hash表不一定要有映射, 只要证明出现过就行, hashset也可以.
- 所有数组元素入hash表(HashSet)
- 遍历数组元素num, 如果num-1不在hash表中,说明可以用num作为一个子序列的头, 循环开始找num++是不是在hash表中,累计连续子序列的长度count. 不用把找到的数从hash表中删除
public int longestConsecutive(int[] nums) {
Set<Integer> hash = new HashSet<Integer>();
for (int num : nums) {
hash.add(num);
}
int ans = 0;
for (int i = 0; i < nums.length; i++) {
int num = nums[i], count = 0;
if (!hash.contains(num - 1)) {
while (hash.contains(num)) {
count++;
num++;
}
}
ans = Math.max(ans, count);
}
return ans;
}
方法2. 并查集
参考:这里
并查集主要的作用是 判断给定的一组数据是不是同一归属(祖先根节点是同一个)
就是在遍历数组的时候,比如遍历到num的时候,“查” nums是否在集合中,如果在就合"并"num和num-1
本题并查集DisjointSet的 数据结构:
Map<Integer, int[]> fatherMap
Key: 当前节点的数值,
value: int[2] 长度2的数组,
[0]:存key的根节点数值,
[1]:存key根节点所在树包含节点个数
class Solution {
public int longestConsecutive(int[] nums) {
if (nums.length < 2) return nums.length;
DisjointSet djSet = new DisjointSet(nums); //初始化并查集
return djSet.getAns();
}
}
class DisjointSet { // 并查集 定义
private Map<Integer, int[]> fatherMap;
private int ans;//存所有 根节点所在树的 节点个数最大值
DisjointSet(int[] nums) {
//初始化1 因为如果数组都是重复数字,第二个for得if无效,就不会更新ans
ans = 1;
fatherMap = new HashMap<Integer, int[]>();
for (int num : nums) {
fatherMap.put(num, new int[]{num, 1});
}
for (int num : nums) {
if (fatherMap.containsKey(num - 1)) {
// num-1和num都在的话, 合并两个树
union(num, num - 1);
}
}
}
int findFather(int child) {// 查找child节点的根节点
int[] root = fatherMap.get(child);
if (root[0] == child)
return child;
int father = findFather(root[0]);
root[0] = father;//直接把chile连接到根节点,压缩树高
return father;
}
void union(int a, int b) { // 合并 b树 到 a 树
int aFather = findFather(a);
int bFather = findFather(b);
if (aFather == bFather) return;
int[] aroot = fatherMap.get(aFather);
int[] broot = fatherMap.get(bFather);
broot[0] = aFather; // 更新b树根节点为a树根节点
aroot[1] = aroot[1] + broot[1]; // 更新a树 节点总数
ans = Math.max(ans, aroot[1]); // 找到最大的节点数
}
int getAns() {return ans;}
}