本体的目标是找到数组中若按顺序排列后缺少的最小的正数
本题的难点在于如何使用常数级的空间以及O(n)级别的时间复杂度实现。
首先需要明确的是,缺失的最小正整数一定出现在[1, nums.length+1]范围内,若数组中包含[1, nums.length]的数字,此时数组若排序则是[1, nums.length]连续的,则缺失的最小正整数为nums.length+1,其他情况(即数组排序后不连续)的最小缺失正整数均在[1, num.length]范围内。
由于时间和空间复杂度的限制,我们需要将数组本身进行一些处理,使其变成哈希存储的形式。
整个过程分成两步:
1.第一次遍历整个数组,将nums[i]放在nums[i]-1的索引处,即映射关系是num[i] = i + 1。处理的方式是,交换nums[i]和nums[nums[i]-1]两位置的数字。交换之后,nums[i]的数值仍可能需要交换到正确位置,因此用一个while循环,循环的条件如下:
1 <= nums[i] <= nums.length
nums[i] != nums[nums[i] - 1]
我们的目的是将处在[1, nums.length]范围内的正数放在对应的索引处,超出此范围的数字无需考虑,第二个条件用于避免在某些数字有重复或已经在正确位置上的数字仍然进入while循环交换位置。
举个简单例子:
遍历完成之后,数组中处在[1, nums.length]范围内的数字全部放在正确的哈希索引位置,接着进行第二步的遍历即可。
2.第二次遍历即遍历已经处理完成的数组,数组中所有处于[1. num.length]范围内的数值,均在对应的位置上,因此从头开始遍历,找到第一个不满足映射关系的索引,加一之后即为缺失的最小的数值。
完整代码如下:
public static int firstMissingPositive(int[] nums) {
// 第一遍遍历将数组映射为哈希表的形式
for (int i=0; i < nums.length;i++) {
// 将[1, N]的数字x映射到数组中x-1的位置上
while (nums[i] >= 1 && nums[i] <= nums.length && nums[nums[i] - 1] != nums[i]) {
int tmp = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = tmp;
}
}
for (int i = 0; i < nums.length; i++){
if (nums[i] != i + 1){
return i + 1;
}
}
return nums.length+1;
}