41.寻找缺失的第一个正数:原地哈希算法详解
文章目录
- 引言
- 问题描述
- 方法思路:原地哈希算法
- 算法步骤
- 完整代码实现
- 关键代码解析
- 复杂度分析
- 示例说明
- 总结
引言
在算法面试和数据处理中,寻找缺失的第一个正数是一个经典问题。题目要求给定一个未排序的整数数组,找到其中缺失的最小正整数,且时间复杂度需为O(n),空间复杂度为O(1)。本文将详细解析如何通过原地哈希算法高效解决这一问题,并提供完整的Java代码实现。
问题描述
给定一个包含n
个整数的数组nums
,找出其中没有出现的最小的正整数。
示例:
- 输入:
nums = [1,2,0]
,输出:3
- 输入:
nums = [3,4,-1,1]
,输出:2
限制条件:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
方法思路:原地哈希算法
传统方法(如排序或哈希表)无法满足空间复杂度要求。原地哈希的核心思想是将数值映射到数组索引上,通过交换操作将每个正整数放到其“正确位置”。
算法步骤
-
处理数组元素
- 遍历数组,若当前元素
nums[i]
是正整数且在[1, n]
范围内,则将其交换到索引nums[i]-1
的位置。 - 使用
while
循环确保交换后的新元素也被正确处理。
- 遍历数组,若当前元素
-
查找缺失值
- 再次遍历数组,第一个满足
nums[i] != i+1
的位置即为缺失的最小正数。 - 若所有位置均正确,则缺失值为
n+1
。
- 再次遍历数组,第一个满足
完整代码实现
public class FirstMissingPositive {public int firstMissingPositive(int[] nums) {int n = nums.length;// 原地哈希:将数值x交换到索引x-1的位置for (int i = 0; i < n; i++) {while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {swap(nums, i, nums[i] - 1);}}// 查找第一个不满足nums[i] == i+1的位置for (int i = 0; i < n; i++) {if (nums[i] != i + 1) {return i + 1;}}return n + 1;}// 交换数组中的两个元素private void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}
关键代码解析
-
原地哈希处理
while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {swap(nums, i, nums[i] - 1); }
- 条件1:
nums[i]
必须是有效正整数(1 ≤ x ≤ n)。 - 条件2:若目标位置的值已正确,则无需交换。
- 循环处理:交换后,新的
nums[i]
可能仍需调整,因此使用while
而非if
。
- 条件1:
-
查找缺失值
if (nums[i] != i + 1) {return i + 1; }
- 第一个不满足
nums[i] == i+1
的索引i
,其对应的i+1
即为缺失值。
- 第一个不满足
复杂度分析
- 时间复杂度:O(n)
每个元素最多被交换一次,两次遍历时间复杂度均为O(n)。 - 空间复杂度:O(1)
仅使用常数级别的额外空间。
示例说明
以输入nums = [3,4,-1,1]
为例:
- 原地哈希处理:
- 遍历到
3
,将其交换到索引2的位置,数组变为[-1,4,3,1]
。 - 遍历到
4
,将其交换到索引3的位置,数组变为[-1,1,3,4]
。 - 遍历到
1
,将其交换到索引0的位置,数组变为[1,-1,3,4]
。
- 遍历到
- 查找缺失值:
- 索引1的值为
-1
,不满足nums[1] == 2
,返回2
。
- 索引1的值为
总结
原地哈希算法通过索引映射和元素交换,将时间复杂度优化至O(n),空间复杂度优化至O(1)。该方法不仅高效,还能帮助深入理解数组索引与数值之间的关系。掌握这一算法,可轻松应对类似的高频面试题。