LeetCode128. 冗余连接

题目描述

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

涉及tag

并查集;动态规划

算法思路

方法1:
遍历数组中的元素,设置一个cur,保存nums[i]持续+1的数字(保证连续),去查找cur是否在数组中(判断cur == nums[j]可以设置boolean变量flags辅助判断),如果一直加到x都还在,返回结果为x + 1(算上一开始的nums[i]本身)
方法2:并查集
方法3:并查集优化
方法4:对方法1的优化,避免同一个结合中的后面数字进入while循环

示例代码1

class Solution {
    public int longestConsecutive(int[] nums) {
        int n = nums.length;
        int ans = 0;
        // 遍历数组中的每个元素num
        for (int i = 0; i < n; i++) {
            // 以num为起点,每次+1向后遍历num+1,num+2,num+3...
            int cur = nums[i] + 1;
            while (true) {
                // 标识是否在数组中找到了cur
                boolean flag = false;
                // 在数组中找cur
                for (int j = 0; j < n; j++) {
                    if (nums[j] == cur) {
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    break;
                }
                cur += 1;
            }
            ans = Math.max(ans, cur - nums[i]);
        }
        return ans;
    }
}

示例代码2

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

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

    // 寻找x的父节点,实际上也就是x的最远连续右边界,这点类似于方法2
    public Integer find(int x) {
        // nums不包含x
        if (!parent.containsKey(x)) {
            return null;
        }
        // 遍历找到x的父节点
        while (x != parent.get(x)) {
            // 进行路径压缩,不写下面这行也可以,但是时间会慢些
            parent.put(x, parent.get(parent.get(x)));
            x = parent.get(x);
        }
        return x;
    }

    // 合并两个连通分量,在本题中只用来将num并入到num+1的连续区间中
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return;
        }
        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;
    }
}



示例代码3(并查集哈希优化)

class UnionFind {
    // 记录每个节点的父节点
    private Map<Integer, Integer> parent;
    // 记录节点所在连通分量的节点个数
    private Map<Integer, Integer> count;

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

    // 寻找x的父节点,实际上也就是x的最远连续右边界
    public Integer find(int x) {
        if (!parent.containsKey(x)) {
            return null;
        }
        // 遍历找到x的父节点
        while (x != parent.get(x)) {
            // 进行路径压缩
            parent.put(x, parent.get(parent.get(x)));
            x = parent.get(x);
        }
        return x;
    }

    // 合并两个连通分量,用来将num并入到num+1的连续区间中
    // 返回值为x所在连通分量的节点个数
    public int union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return count.get(rootX);
        }
        parent.put(rootX, rootY);
        // 更新该根结点连通分量的节点个数
        count.put(rootY, count.get(rootX) + count.get(rootY));
        return count.get(rootY);
    }
}
class Solution {
    public int longestConsecutive(int[] nums) {
        // 去除nums为空的特例
        if (nums == null || nums.length == 0) {
            return 0;
        }
        UnionFind uf = new UnionFind(nums);
        int ans = 1;
        // 一次遍历即可
        for (int num : nums) {
            if (uf.find(num + 1) != null) {
                // union会返回num所在连通分量的节点个数
                ans = Math.max(ans, uf.union(num, num + 1));
            }
        }
        
        return ans;
    }
}



示例代码4

class Solution {
    public int longestConsecutive(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, num);
        }
        int ans = 0;
        for (int num : nums) {
            if (map.containsKey(num)) {
                int right = map.get(num);
                while (map.containsKey(right + 1)) {
                    map.remove(right);
                    right = map.get(right + 1);
                }
                map.put(num, right);
                ans = Math.max(ans, right - num + 1);
            }
        }
        return ans;
    }
}

示例代码5

哈希集合,可能有多个相同最小数存在

class Solution {
    public int longestConsecutive(int[] nums) {
        // 建立一个存储所有数的哈希表,同时起到去重功能
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }

        int ans = 0;
        // 遍历去重后的所有数字
        for (int num : set) {
            int cur = num;
            // 只有当num-1不存在时,才开始向后遍历num+1,num+2,num+3......
            if (!set.contains(cur - 1)) {
                while (set.contains(cur + 1)) {
                    cur++;
                }
            }
            // [num, cur]之间是连续的,数字有cur - num + 1个
            ans = Math.max(ans, cur - num + 1);
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值