算法-面试-最长连续序列

算法-面试-最长连续序列

1 题目概述

1.1 题目出处

https://leetcode-cn.com/problems/longest-consecutive-sequence/

1.2 题目描述

给定一个未排序的整数数组,找出最长连续(等差为1)序列的长度。

要求算法的时间复杂度为 O(n)。

示例:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

2 并查集

2.1 思路

遍历每个数字,如果小1或大1的数字存在,就进行并查集join。

最后遍历所有祖先,找出并查集最大的那个。

2.2 代码

class Solution {
    // 过滤重复数字
    private Set<Integer> records = new HashSet<>();
    // 并查集,key为数字本身,value为他的parent
    Map<Integer, Integer> unionFindSet = new HashMap<>();
    
    public int longestConsecutive(int[] nums) {
        int result = 0;
        if(nums == null || nums.length == 0){
            return result;
        }
        
        for(int i = 0; i < nums.length; i++){
            // 过滤重复数字
            if(records.contains(nums[i])){
                continue;
            }
            records.add(nums[i]);
            // 每个数最开始parent就是自己
            unionFindSet.put(nums[i], nums[i]);
            // 和小1的数字并查集Join
            int prev = nums[i] - 1;
            if(unionFindSet.containsKey(prev)){
                join(nums[i], prev);
            }
            // 和大1的数字并查集Join
            int next = nums[i] + 1;
            if(unionFindSet.containsKey(next)){
                join(nums[i], next);
            }
        }
        // 统计每个并查集元素个数
        Map<Integer, Integer> cntMap = new HashMap<>();
        for(int record : records){
            int root = find(record);
            Integer cnt = cntMap.get(root);
            int tmp = 0;
            if(cnt != null){
                tmp = cnt + 1;
            }else{
                tmp = 1;
            }
            cntMap.put(root, tmp);
            // 输出并查集元素最多的那个
            result = Math.max(result, tmp);
        }
        return result;
    }

    private void join(int p, int q){
        int left = find(p);
        int right = find(q);
        if(left == right){
            return;
        }
        unionFindSet.put(left, right);
    }

    private int find(int p){
        int son = p;
        while(unionFindSet.get(p) != p){
            p = unionFindSet.get(p);
        }
        while(unionFindSet.get(son) != son){
            int tmp = unionFindSet.get(son);
            unionFindSet.put(son, p);
            son = tmp;
        }
        return p;
    }
}

2.3 时间复杂度

在这里插入图片描述

2.4 空间复杂度

O(N)

3 两趟遍历-两个方向延伸

3.1 思路

前面并查集方法耗时太长,主要是join时每次需要一直往上查询直到root,还需要路径压缩。

其实我们简单考虑,要找到连续序列,无非就是当遇到数字i时,分别找i-1, i-2, i-3, … 以及 i+1, i+2, i+3…,并将总长累加,同时标记这些元素已访问过。

当然,需要直到这些数字是否在我们的数字集内,就需要先将他们存到一个HashSet,随后就可以O(1)时间取数。

同时,由于还是用了一个HashSet记录已访问过元素,所以每个元素只会被访问一次,总时间复杂度控制在O(N)。

3.2 代码

class Solution {
    // 过滤重复数字
    private Set<Integer> records = new HashSet<>();
    // 记录访问过的元素
    private Set<Integer> visited = new HashSet<>();

    public int longestConsecutive(int[] nums) {
        int result = 0;
        if(nums == null || nums.length == 0){
            return result;
        }

        // 将数字全部过滤
        for(int i = 0; i < nums.length; i++){
            records.add(nums[i]);
        }

        // 遍历不重复数字,往小于1和大于1方向分别延伸,记录总长即可
        // 每个元素只访问了一次
        for(int i : records){
            if(visited.contains(i)){
                continue;
            }
            int total = 1;
            int prev = i - 1;
            while(records.contains(prev)){
                visited.add(prev--);
                total++;
            }
            int next = i + 1;
            while(records.contains(next)){
                visited.add(next++);
                total++;
            }
            result = Math.max(result, total);
        }
        return result;
    }
}

3.3 时间复杂度

在这里插入图片描述
O(N)

  • 这次速度大幅提升。

3.4 空间复杂度

O(N)

4 两趟遍历-一个方向延伸

4.1 思路

前面代码需要分别往大1和小1两个方向延伸,其实我们完全可以找到没有比他小1的那个元素,只需要往大1大那个方向延伸即可。这样都不需要记录元素是否访问过了。

4.2 代码

class Solution {
    // 过滤重复数字
    private Set<Integer> records = new HashSet<>();

    public int longestConsecutive(int[] nums) {
        int result = 0;
        if(nums == null || nums.length == 0){
            return result;
        }

        // 将数字全部过滤
        for(int i = 0; i < nums.length; i++){
            records.add(nums[i]);
        }

        // 遍历不重复数字,当某个数字没有比他小1的数字存在,则开始往大1方向查找
        // 每个元素只访问了一次
        for(int i : records){
            int prev = i - 1;
            if(records.contains(prev)){
                continue;
            }
            int total = 1;
           
            int next = i + 1;
            while(records.contains(next++)){
                total++;
            }
            result = Math.max(result, total);
        }
        return result;
    }
}

4.3 时间复杂度

在这里插入图片描述

4.4 空间复杂度

O(N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值