128. 最长连续序列

一、题目

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

二、思路

所有在一个连续区间的元素都会在一个连通分量中,且该连通分量的根节点为该连续区间中的最大值

  1. 遍历数组,如果nums[i]+1存在,将nums[i]加入nums[i]+1所在的连通分量中
  2. 重新遍历数组,通过find函数找到nums[i]所在分量的根节点,也就是其所在连续区间的最大值,从而求出连续区间的长度

三、代码

class UnionFind {
    /**
     * 记录每个节点的父节点
     */
    private Map<Integer, Integer> parent;

    public UnionFind(int[] nums) {
        parent = new HashMap<>();
        for (int num : nums) {
            // 初始化父节点为自身
            parent.put(num, num);
        }
    }

    /**
     * 寻找x的父节点,实际上也就是x的最远连续右边界,也就是其所在连续区间的最大值
     * @param x
     * @return
     */
    public Integer find(int x) {
        if (!parent.containsKey(x)) {
            return null;
        }

        while (x != parent.get(x)) {
            // 路径压缩
            parent.put(x, parent.get(parent.get(x)));
            x = parent.get(x);
        }
        return x;
    }

    /**
     * 合并两个连通分量,在本题中只用来将num并入到num+1的连续区间中
     * @param x
     * @param y
     */
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return ;
        }
        // 将num所在树的根节点设置为num+1所在树的根节点
        parent.put(rootX, rootY);
    }
}
class Solution {
    public int longestConsecutive(int[] nums) {
        UnionFind uf = new UnionFind(nums);
        int ans = 0;
        
        for (int num : nums) {
            // 当num+1存在,将num合并到num+1所在集合中
            if (uf.find(num + 1) != null) {
                uf.union(num, num + 1);
            }
        }

        for (int num : nums) {
            // 找到num的最远连续右边界,也就是其所在连续区间的最大值
            int right = uf.find(num);
            ans = Math.max(ans, right - num + 1);
        }
        return ans;
    }
}

四、分析

  • 时间复杂度:O(n),路径压缩后的并查集时间复杂度近似为O(1)
  • 空间复杂度:O(n)
posted @ 2023-04-29 19:33  ImreW  阅读(22)  评论(0编辑  收藏  举报