LeetCode Top 100 Liked Questions 128. Longest Consecutive Sequence (Java版; Hard)

welcome to my blog

LeetCode Top 100 Liked Questions 128. Longest Consecutive Sequence (Java版; Hard)

题目描述
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

Your algorithm should run in O(n) complexity.

Example:

Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
class Solution {
    public int longestConsecutive(int[] nums) {
        int n = nums.length;
        if(n<=1){
            return n;
        }
        HashSet<Integer> set = new HashSet<>();
        for(int a : nums){
            set.add(a);
        }
        int max = 1;
        //虽然是两层循环, 但是每个元素最多访问两次, 所以时间复杂度是O(N)
        for(int i=0; i<n; i++){
            int cur = nums[i];
            //如果不存在nums[i]-1, 说明nums[i]是一个序列的最小值, 接下来看看这个序列的长度
            if(!set.contains(cur-1)){
                int len = 1;
                while(set.contains(cur+1)){
                    len++;
                    cur++;
                }
                max = Math.max(max, len);
            }
        }
        return max;
    }
}
第一次做; 使用哈希表, 将暴力解法变成了时间复杂度为O(n)的最优解;使用哈希表并没有增加复杂度, 因为使用哈希表进行增删改查, 可以认为时间复杂度是O(1); 使用有序表(红黑树,AVL树,SB树,跳表)的大部分操作()都是O(logn)的, 少数是O(1)的; 理解如何去找子序列的下界; 理解为什么每个元素最多被访问两次
/*
暴力法之所以费时间是因为每个元素都可能被访问多次, 实际上我们在找子序列时, 希望从子序列的开头开始遍历, 那么如何找到子序列的开头呢?
可以借助哈希表, 记录数组中的所有元素, 遍历每一个元素时, 如果哈希表中包含nums[i]-1这个元素, 说明nums[i]不是子序列的开头,也就是说,
子序列的开头是这样的元素: a在哈希表中, 但是a-1不在哈希表中
找到了子序列的开头元素,接着开始使用while循环找上界; 
虽然存在循环嵌套, 但是数组中每个元素最多访问两次: 第一次是检查是否可以作为子序列的开头; 第二次是寻找某个子序列的上界时可能会被访问
核心:
1. 理解如何找子序列的下界
2. 理解为什么数组中每个元素最多访问两次

脑子别僵了, O(n)的时间复杂度不是说只遍历一遍数组, 可以遍历多次数组的
*/
import java.util.Set;
import java.util.HashSet;

class Solution {
    public int longestConsecutive(int[] nums) {
        //input check
        if(nums==null || nums.length==0)
            return 0;
        Set<Integer> hs = new HashSet<>();
        for(int num : nums)
            hs.add(num);//这样是不是会自动处理重复值
        int currLen = 1, maxLen = 1, a;
        for(Integer num : hs){
            //找子序列的下界
            if(!hs.contains(num-1)){
                a = num;
                currLen = 1;
                //内循环会使得某些元素被访问两次
                while(hs.contains(a+1)){
                    currLen++;
                    a++;
                }
                maxLen = Math.max(maxLen, currLen);
            }
        }
        return maxLen;
    }
}
第一次做; 时间复杂度取决于排序算法, O(nlogn); 理解maxLen和currLen的初始化; 配合案例[1,2,0,1]理解如何处理nums[i]==nums[i-1]的情况
/*
先来个O(nlogn)的算法
先排序,再从头开始遍历
绊脚案例:
[1,2,0,1]   3
*/
import java.util.Arrays;

class Solution {
    public int longestConsecutive(int[] nums) {
        if(nums==null || nums.length==0)
            return 0;
        Arrays.sort(nums);
        //因为input check后数组中至少有一个元素, 所以将maxLen和currLen初始化为1
        int maxLen = 1, currLen = 1;
        //当前元素和前一个元素比较
        for(int i=1; i<nums.length; i++){
            if(nums[i]==nums[i-1]){
                //想起楚为什么nums[i]==nums[i-1]时不需要将currLen置1, 案例提示:[1,2,0,1]   3
                continue;
            }
            if(nums[i]==nums[i-1] + 1){
                currLen++;
                maxLen = Math.max(maxLen, currLen);
            }
            else{                
                currLen=1;
            }
        }
        return maxLen;
    }
}
题解, 暴力解法; 理解O(n^3)是怎么来的; 注意for循环和while循环的写法
  • 遍历每一个数, O(n)
  • 对于每一个数a, 判断a+1是否在数组中, O(n)
  • 输入是[1,2,3,4,5]时, 对于1, 需要判断2是否在数组中, 3是否在数组中,…5是否在数组中, 6是否在数组中, 需要判断O(n)次
  • 上面三步整合在一起就是O(n^3)
class Solution {
    public int longestConsecutive(int[] nums) {
        int longestStreak = 0;
        for (int num : nums) {
            int currentNum = num;
            int currentStreak = 1;

            while (arrayContains(nums, currentNum + 1)) {
                currentNum += 1;
                currentStreak += 1;
            }

            longestStreak = Math.max(longestStreak, currentStreak);
        }

        return longestStreak;
    }
    
    private boolean arrayContains(int[] arr, int num) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == num) {
                return true;
            }
        }
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值