41 First Missing Positive
题目
题解
题目要求找到输入数组中未出现的最小正整数,同时要求:
- 时间复杂度为O(n)
- 只使用O(1)的额外空间
针对要求1,很容易想到使用计数排序的方法:开辟一个大小为K的计数数组(K为输入数据的范围大小),对输入数组进行计数排序,对排序后的数组从小到大进行扫描,即可找到未出现的最小正整数。
基于上述思路还能进一步优化:不进行排序,而且只对正整数进行计数。从左到右对计数数组进行扫描,当计数为0时返回当前下标(计数为0代表该正整数没有出现在输入数组),或者在扫描结束后返回下标加一(此时有两种情况:一、输入数组中没有出现正整数,此时返回值等于0+1;二、输入数组中的正整数包含了从1到最大值的所有正整数,此时返回值等于最大值+1)。
但是上述方法均不满足只使用O(1)的额外空间的要求2。为了满足要求2,我们需要充分利用输入数组本身。记输入数组为A,其大小为N,而A中未出现的最小正整数必然在区间[1,N+1]内,这意味着我们利用A本身对落在区间[1,N]内的正整数进行计数操作即可得出结果。
但是,直接写入计数值会造成数组信息丢失,为了保留数组信息我们只能进行交换操作。注意到,对于区间[1,N]内的一个正整数num,记它的计数值为C[num],我们只需要区分两种情况,即C[num]是等于0还是大于0,因此计数值的具体数值是不必要的,我们只需要一个能够区分这两种情况的标记就行了。当出现num时,一个合理的标记是使得A[num-1]=num,而这仅通过交换也能够做到。为了标记所有[1,N]内的正整数,需要对数组A从左到右不停通过交换操作对下标i上的数字进行标记直到:①i不满足1 <= A[i] <= N,或者②位置A[i]-1已被标记好。
在上述循环操作结束后,从左向右扫描A,在标记不成立,也就是A[num-1]不等于num时返回下标加一,或者在扫描结束后返回下标加一,就能得出所求结果。
代码
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int total = nums.size();
if (total == 0) {
return 1;
}
int i;
for (i = 0; i < total; ++i) {
while (nums[i] > 0 && nums[i] <= total
&& nums[i] != nums[nums[i] - 1]) {
swap(nums[i], nums[nums[i] - 1]);
}
}
for (i = 0; i < total; ++i) {
if (nums[i] != i + 1) {
break;
}
}
return i + 1;
}
};