Leetcode Hot100系列128. 最长连续序列

1. 128. 最长连续序列

1.1 思路

  • 题目要设计时间复杂度为O(n)的算法解决此问题。考虑枚举数组中的每个数 x x x,考虑其为起点,不断尝试匹配 x + 1 , x + 2 , . . . x+1,x+2,... x+1x+2...,是否存在,假设最长匹配到了 x + y x+y x+y,那么其长度为 y + 1 y+1 y+1
  • 匹配的过程,暴力的方法是 O ( n ) O(n) O(n)遍历数组去看它后面连接的数是否存在,更高效的方法是用一个哈希表存储数组中的数,这样查看一个数是否存在即能优化至 O ( 1 ) O(1) O(1)的时间复杂度。但是这样算法的时间复杂度最坏情况还是会达到 O ( n 2 ) O(n^2) O(n2)(外层需要枚举 O ( n ) O(n) O(n)个数,内层暴力匹配也需要 O ( n ) O(n) O(n)次)。无法满足题目的要求,如果已知一个 x , x + 1 , x + 2 , . . . , x + y x,x+1,x+2,..., x +y xx+1x+2...,x+y的连续序列,我们就不需要从 x + 1 , x + 2 , x + y x+1,x+2,x+y x+1x+2x+y处尝试开始匹配,因为这个结果一定不会优于从 x x x开始的连续序列,因此,在外层循环碰到这种情况直接跳过即可。
  • 如何判断是否为这种情况?枚举的数 x x x 一定是在数组中不存在前驱数 x − 1 x-1 x1的,否则我们可以从 x − 1 x-1 x1开始匹配,没必要从 x x x开始匹配。因此,每次在哈希表中检查是否存在 x − 1 x-1 x1,即能判断是否需要跳过。

1.2 代码实现

class Solution {
    public int longestConsecutive(int[] nums) {
    	Set<Integer> set = new HashSet<>();
    	for (int num : nums) {
    		set.add(num);
    	}
    	int len = 0;
    	int maxLen = 0;
    	int numCur = 0;
    	for (int num : set) {
    		if (!set.contains(num - 1)) {
    			numCur = num;
    			while(set.contains(numCur + 1)) {
    				numCur++;
    			}
    			len = numCur - num + 1;
    			maxLen = Math.max(len, maxLen);
    		}
    	}
    	return maxLen;
    }
}

1.3 思考

  • 在题目要求考虑时间复杂度时,可以考虑 哈希表 这种用空间换时间的思想。

2. 448. 找到所有数组中消失的数字

2.1 思考

  • 方法一:首先想到的就是使用哈希表,此题可以直接使用数组存储,nums数组中出现的数字 i i i就令numbers数组中下标为 i − 1 i-1 i1的数组元素为1,证明在nums数组中有元素 i i i
  • 方法二:题目要求不要使用额外的空间,且假定返回的数组不算在额外空间内,所以要想到 原地修改,nums的长度也是n,让nums充当哈希表即可。遍历数组nums,每遇到一个数 x x x,就让 n u m s [ x − 1 ] nums[x-1] nums[x1]增加 n n n,由于nums中所有的数均在 [ 1 , n ] [1,n] [1,n]中,增加以后,这些数必然大于 n n n。最后再遍历nums,如果 n u m s [ i ] nums[i] nums[i]未大于 n n n,就说明没有遇到过数 i + 1 i + 1 i+1。这样就能找到缺失的数。

2.2 代码实现

  • 方法一代码实现
class Solution {
    public List<Integer> findDisappearedNumbers(int[] nums) {
    	int n = nums.length;
    	int[] numbers = new int[n];
    	for (int num : nums) {
    		numbers[num - 1] = 1;
    	}
    	List<Integer> list = new ArrayList<>();
    	for (int i = 0; i < n; i++) {
    		if (numbers[i] == 0) {
    			list.add(i + 1);
    		}
    	}
    	return list; 
    }
}
  • 方法二代码实现
class Solution {
    public List<Integer> findDisappearedNumbers(int[] nums) {
    	List<Integer> res = new ArrayList<>();
        int n = nums.length;
        for (int num : nums) {
        	int x = (num - 1) % n;
        	nums[x] += n;
        }
        for (int i = 0; i < n; i++) {
        	if (nums[i] <= n) {
        		res.add(i + 1);
        	}
        }
        return res;
    }
}

说明:

  1. 为什么要减一然后对n取余,首先说减一是因为 n u m s nums nums数组中的元素取值范围是 1 ∼ n 1 \sim n 1n,而 n u m s nums nums数组的下标是 0 ∼ n − 1 0 \sim n-1 0n1,所以要保持一一对应就要让元素值减一。而对 n n n取余是因为每次给元素值加 n n n可能导致元素的值大于 n − 1 n - 1 n1,要超出数组下标,每次加 n n n,要想不超出对 n n n取余就行。
  2. 在第二个for循环中, n u m s [ i ] < = n nums[i]<=n nums[i]<=n而不是 n u m s [ i ] < n nums[i] < n nums[i]<n是因为当元素值为n的时候说明根本没加,因为原本的值都大于0,存在该元素的话加上 n n n,一定要大于 n n n。所以,小于等于 n n n就代表 i + 1 i +1 i+1在原来的数组中不存在。

2.3 思考

  • 要空间复杂度为 O ( 1 ) O(1) O(1)就要考虑 原地修改
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值