1. 题目

读题
考查点
2. 解法
两种解法
- 使用hashSet
- 使用并查集
解法一:使用hashSet
思路
这个解法的核心思想是
- 找出数组中的所有连续序列的起点,
- 然后从每个起点开始向后延伸,计算出每个序列的长度,并更新最长长度。
- 为了找出所有的起点,我们需要判断一个元素是否有前驱,即它的前一个元素是否在数组中。
- 如果没有前驱,说明它是一个起点,
- 否则它已经被之前的序列包含了,不需要再考虑。
- 为了快速判断一个元素是否有前驱,我们使用了HashSet来存储数组中的所有元素,并利用它的O(1)的查找操作。
- 为了从每个起点开始向后延伸,我们需要判断一个元素是否有后继,即它的后一个元素是否在数组中。
- 如果有后继,说明序列还可以继续,否则序列结束了。
- 我们同样使用HashSet来判断一个元素是否有后继,并用一个变量来记录当前序列的长度。
- 每次找到一个新的起点,我们就重置这个变量为1,并开始向后延伸,每次延伸时将这个变量加1,并与最长长度比较,
- 如果更大就更新最长长度。
- 这样,我们就可以在一次遍历数组的过程中,找出最长连续元素序列的长度。
代码逻辑
- 第1-4行,我们创建了一个HashSet,并将数组中的所有元素添加到其中,这样我们就可以在O(1)的时间内判断一个元素是否在数组中。
- 第6行,我们初始化了一个变量longest,用来记录最长连续元素序列的长度,初始值为0。
- 第8-18行,我们遍历了数组中的每个元素,对于每个元素num,我们执行以下操作:
- 第9-10行,我们检查num是否是一个连续序列的起点,即它的前一个元素num-1是否不在HashSet中。如果是,我们就进入第11-17行的逻辑,否则我们就跳过这个元素,因为它已经被之前的序列包含了。
- 第11-16行,我们从num开始向后寻找连续的元素,并记录当前序列的长度。我们用一个变量current来表示当前访问的元素,初始值为num,用一个变量length来表示当前序列的长度,初始值为1。然后我们进入一个循环,每次循环中,我们检查current+1是否在HashSet中,如果是,说明序列还可以继续,我们就将current和length都加1,并继续循环;如果不是,说明序列结束了,我们就跳出循环。
- 第17行,我们将当前序列的长度和最长长度进行比较,如果当前序列更长,就更新最长长度。
- 第19行,我们返回最长长度作为答案。
具体实现
class Solution {
public int longestConsecutive(int[] nums) {
// 创建一个HashSet存储数组中的所有元素
Set<Integer> set = new HashSet<>();
for (int num : nums) {
set.add(num);
}
// 初始化最长长度为0
int longest = 0;
// 遍历数组
for (int num : nums) {
// 检查num是否是一个连续序列的起点
if (!set.contains(num - 1)) {
// 从num开始向后寻找连续的元素
int current = num;
int length = 1;
while (set.contains(current + 1)) {
current++;
length++;
}
// 更新最长长度
longest = Math.max(longest, length);
}
}
// 返回最长长度
return longest;
}
}
解法二:使用并查集
思路
这个问题是要找到一个无序数组中最长的连续序列的长度。
你可以使用并查集(union find)的数据结构来解决这个问题。并查集是一种能够快速合并和查询集合的数据结构。
- 你可以用一个数组来表示每个元素所属的集合,
- 初始时每个元素都是自己的集合。
- 然后你可以遍历数组,对于每个元素,
- 如果它的前一个或后一个元素也在数组中,就把它们所属的集合合并起来。
- 合并的时候,你需要找到每个集合的根节点,然后把其中一个根节点指向另一个根节点。
- 同时,你还需要维护一个哈希表,记录每个集合的大小。
- 最后,你只需要遍历哈希表,找到最大的集合大小,就是答案了。
具体实现
class Solution {
public int longestConsecutive(int[] nums) {
// 如果数组为空,返回0
if (nums == null || nums.length == 0) return 0;
// 初始化并查集数组和哈希表
int n = nums.length;
int[] parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
HashMap<Integer, Integer> map = new HashMap<>(); // key: 数组元素,value: 元素在并查集数组中的索引
HashMap<Integer, Integer> size = new HashMap<>(); // key: 集合根节点在并查集数组中的索引,value: 集合大小
// 遍历数组
for (int i = 0; i < n; i++) {
int num = nums[i];
// 如果元素已经出现过,跳过
if (map.containsKey(num)) continue;
// 把元素和它在并查集数组中的索引放入哈希表
map.put(num, i);
// 初始化该元素所属集合的大小为1
size.put(i, 1);
// 如果元素的前一个或后一个元素也在数组中,就把它们所属的集合合并起来
if (map.containsKey(num - 1)) {
union(parent, size, map.get(num - 1), i);
}
if (map.containsKey(num + 1)) {
union(parent, size, map.get(num + 1), i);
}
}
// 遍历哈希表,找到最大的集合大小
int max = 0;
for (int s : size.values()) {
max = Math.max(max, s);
}
return max;
}
// 合并两个集合的函数
private void union(int[] parent, HashMap<Integer, Integer> size, int x, int y) {
// 找到两个集合的根节点
int rootX = find(parent, x);
int rootY = find(parent, y);
// 如果根节点相同,说明已经在同一个集合中,不需要合并
if (rootX == rootY) return;
// 把其中一个根节点指向另一个根节点
parent[rootX] = rootY;
// 更新合并后的集合大小
size.put(rootY, size.get(rootX) + size.get(rootY));
}
// 找到一个元素所属集合的根节点的函数
private int find(int[] parent, int x) {
// 如果元素不是自己的父节点,就沿着父节点向上找,直到找到根节点
while (x != parent[x]) {
// 顺便把沿途的元素都指向根节点,压缩路径,加速查找
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
}
浙公网安备 33010602011771号