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

本文探讨了如何使用哈希表在O(n)时间复杂度内解决两个数组问题:寻找最长连续序列和查找消失的数字。在最长连续序列问题中,通过跳过已有前驱数的元素来优化枚举过程;在查找消失的数字问题中,通过原地修改数组实现空间复杂度为O(1)的解决方案。这两个问题都体现了用空间换取时间的有效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)就要考虑 原地修改
### LeetCode Hot 100 Problems 列表 LeetCode 的热门题目列表通常由社区投票选出,涵盖了各种难度级别的经典编程挑战。这些题目对于准备技术面试非常有帮助。以下是部分 LeetCode 热门 100 题目列表: #### 数组与字符串 1. **两数之和 (Two Sum)** 2. **三数之和 (3Sum)** 3. **无重复字符的最长子串 (Longest Substring Without Repeating Characters)** 4. **寻找两个正序数组的中位数 (Median of Two Sorted Arrays)** #### 动态规划 5. **爬楼梯 (Climbing Stairs)** 6. **不同的二叉搜索树 (Unique Binary Search Trees)** 7. **最大子序列和 (Maximum Subarray)** #### 字符串处理 8. **有效的括号 (Valid Parentheses)** 9. **最小覆盖子串 (Minimum Window Substring)** 10. **字母异位词分组 (Group Anagrams)** #### 图论 11. **岛屿数量 (Number of Islands)** 12. **课程表 II (Course Schedule II)** #### 排序与查找 13. **最接近原点的 K 个点 (K Closest Points to Origin)** 14. **接雨水 (Trapping Rain Water)** 15. **最长连续序列 (Longest Consecutive Sequence)[^2]** #### 堆栈与队列 16. **每日温度 (Daily Temperatures)** 17. **滑动窗口最大值 (Sliding Window Maximum)** #### 树结构 18. **验证二叉搜索树 (Validate Binary Search Tree)** 19. **二叉树的最大路径和 (Binary Tree Maximum Path Sum)** 20. **从前序与中序遍历序列构造二叉树 (Construct Binary Tree from Preorder and Inorder Traversal)** #### 并查集 21. **冗余连接 II (Redundant Connection II)** #### 贪心算法 22. **跳跃游戏 (Jump Game)** 23. **分割等和子集 (Partition Equal Subset Sum)** #### 双指针技巧 24. **环形链表 II (Linked List Cycle II)[^1]** 25. **相交链表 (Intersection of Two Linked Lists)** #### 其他重要题目 26. **LRU缓存机制 (LRU Cache)** 27. **打家劫舍系列 (House Robber I & II)** 28. **编辑距离 (Edit Distance)** 29. **单词拆分 (Word Break)** 此列表并非官方发布版本而是基于社区反馈整理而成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值