剑指 Offer II 119. 最长连续序列——一题多解哈希表+并查集Java

目录

1.题目

2.思路

方法1——排序nlogn

方法2——哈希表O(n)

时间复杂度——O(n)

空间复杂度——O(n)

方法3——并查集O(n)

时间复杂度——O(logn)

空间复杂度——O(n)

1.题目

2.思路

方法1——排序nlogn

这个题第一个思路直接就是先排序这个数组,然后找到最长的连续值就可以。时间复杂度为O(nlogn),题目中要求为O(n)的话,需要进一步思考.

方法2——哈希表O(n)

主要借助哈希表查找一个元素是O(1)的时间复杂度。

  • 先用一个哈希表记录每个元素是否出现过(用set也是可行,达到去重目的)。
  • 然后判断当前元素num是否已经访问过
  • 如果没有访问过,用一个left 和 right指针指向最长连续字符的左右边界。
  • 如果num+ 1出现在哈希表中,并且没有访问过,那么就更新right, 更新num++,继续判断。
  • 如果num - 1出现在哈希表中,并且没有访问过,那么就更新left, 更新num---,不断循环。
  • 更新ans = Math.max(ans, right - left + 1)

时间复杂度——O(n)

虽然有两个while,但是可以保证每个元素只会访问一次,所以是O(n)

空间复杂度——O(n)

使用了一个哈希表,所以是O(n)

class Solution {
    public int longestConsecutive(int[] nums) {
        int n = nums.length;
        Map<Integer,Integer>map = new HashMap();
        for(int num : nums){
            map.put(num, 1);
        }

        int ans = 0;
        int left =0, right = 0;
        for(int num : map.keySet()){
            if(map.get(num) == 1){
                // 没有被访问过
                // map.put(nums[i], 0);
                right = num;
                left = num;
                int temp = left;
                while(map.containsKey(temp) && map.get(temp) == 1){
                    if(temp != num)
                        map.put(temp, 0);
                    left = temp;
                    temp--;
                }
                temp = right;
                while(map.containsKey(temp) && map.get(temp) == 1){
                    if(temp != num)
                        map.put(temp, 0);
                    right = temp;
                    temp++;
                }
                // System.out.println(nums[i] +">>>" + left +">>>" + right);
                ans = Math.max(ans, right - left + 1);
            }
        }
        return ans;
    }
}

方法3——并查集O(n)

并查集的思路,值得学习。并查集一般有两个函数和一个数组parent(或者是一个哈希表,用来记录每个节点的祖宗节点),一个是find(int x),用来找到节点x的祖宗节点。一个是union(x, y),用来连接节点x 和 节点 y,主要是更新两个节点的祖宗节点。

  • 初始化哈希表map(num, num),一开始每个节点的祖宗节点就是自己本身。
  • 遍历哈希表map, 如果num + 1 存在哈希表中,那么union(num, num + 1),如果num - 1存在哈希表中,那么union(num - 1, num).
  • 最后再遍历哈希表,答案就是每个节点的祖宗节点 - 本身 + 1中的最大值。也就是ans = Math.max(ans, find(num) - num + 1)

时间复杂度——O(logn)

注意find(int x)中有while循环中有路径压缩。map.put(cur, find(cur));好像时间是介于O(1)和O(logn)之间,但是现实是运行时间非常满,141ms.可能跟合并操作有关系。

虽然这个题的并查集运行时间比较长,但是这个思路非常有意思,并查集需要学习!

空间复杂度——O(n)

有哈希表O(n)

class Solution {
    // 记录每个节点的祖宗节点
    Map<Integer, Integer>map = new HashMap();
    public int longestConsecutive(int[] nums) {
        // 初始化祖父节点
        int n = nums.length;
        for(int num : nums){
            map.put(num, num);
        }
        if(map.keySet().size() <= 1) return map.keySet().size();
        // 遍历数组,连接节点
        for(int i = 0 ; i < n ; i++){
            if(map.containsKey(nums[i] - 1))
                union(nums[i] - 1, nums[i]);
            if(map.containsKey(nums[i] + 1))
                union(nums[i], nums[i] + 1);
        }

        // 更新ans
        int ans = 0;
        for(int num : nums){
            ans = Math.max(ans, find(num) - num + 1);
        }
        return ans;
    }

    // 找到祖宗节点
    public int find(int x){
        while(x != map.get(x)){
            int cur = map.get(x);
            map.put(cur, find(cur));
            x = cur;
        }
        return x;
    }

    // 连接两个节点x y
    public void union(int x, int y){
        int parentX = find(x);
        int parentY = find(y);
        if(parentX != parentY)
            map.put(parentX, parentY);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值