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次,其实是没问题的。

posted @ 2024-10-16 14:51  名字好难想zzz  阅读(22)  评论(0)    收藏  举报