41.缺失的第一个正数
这道题能够学习到如何高效的利用数组来表达信息。
实际上,数组能够承载信息的有:
- 数组元素值
- 数组元素值的符号,正负可以代表不同的信息
- 由于数组本来就是按顺序存储的,其下标或者我们存储元素的位置,能够表达帮助表达顺序
具体到这题,实际上我们需要从1递增寻找正整数,直到出现第一个不连续的正整数,比如1,2,3,5.这里4断开了,因此4就是最后的结果,而5,6,7,8.这里1开始就断开了,因此答案就是1。
因此我们想到,可以将元素对应到数组位置,通过那个位置存储值的符号来表达该元素是否在数组中出现。比如3,我们将它对应到数组下标为2的位置。
1、数组下标范围是否支持表示所有可能的解:如果出现了大于数组长度的值,它不可能是最后的答案,因为它之前的数肯定是不连续的。
2、如何确保不遗漏、不重复。这里由于我们需要处理变更数组元素的符号,无论是用正还是负,都会影响数组实际上值的结果,因为我们没有额外的信息来获知在后面的元素的符号有没有被修改,因此我们先处理一下数组,将所有负数和0移到尾部,然后统计正整数个数,这里的时间复杂度显然是O(N)。然后按序处理所有满足条件的正数,计算对应的位置,将该位置的元素置负,表示该位置对应的正整数在数组中出现,,而为正数的位置则是缺失的元素对应位,0和负数不需要处理,同样表示占位。
2、用哪个符号来表示出现,见2。
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
auto partition_point = std::partition(nums.begin(), nums.end(), [] (int x) {return x > 0; });
int count = 0;
int flag = 0;
for (auto num : nums) {
if (num > 0) {
count++;
}
}
int n = nums.size();
for(int i = 0; i < n; i++) {
//大于数组长的正整数不需要处理,后续的负数和0不需要处理,它们不会引起歧义。同时使用count来确保所有正数都被处理,因为正数可能被修改为负数
if( abs( nums[i] ) <= n && i < count ) {
int temp = abs( nums[i] );
if ( nums[ temp - 1 ] > 0) {//将表示出现的正符号修改为负
nums[temp- 1] = -1 * nums[temp - 1] ;
}
}
}
//最后得到的数组中,元素>0 表示该位置的对应的值未出现过
for (int i = 0; i < n ; i++) {
//第一个大于0的元素表示最小的,未出现的正整数
if (nums[i] >0) {
flag = i + 1;
break;
}
//如果全部是负数或0,说明前面的正数序列是从1开始的连续序列,因此返回出现过的正整数的最大值+1
else if( i==(n-1) )
flag = count+1;
}
return flag;
}
};
后面看了题解,其实可以把负数换成n+1,能够减少一个partition函数的时间。代码也会简单很多。
第二种的替换方法最开始就有这个想法,但是觉得替换起来时间复杂度会超,看完题解发现确实最多就替换n次,其实是没问题的。

浙公网安备 33010602011771号