lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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;
    }
}

  

 

 

3. 总结

posted on 2023-05-02 19:26  白露~  阅读(55)  评论(0编辑  收藏  举报