41. 缺失的第一个正数

toc

题目描述

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
示例 2:

输入:nums = [3,4,-1,1]
输出:2
示例 3:

输入:nums = [7,8,9,11,12]
输出:1

提示:

1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-missing-positive
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

此题在LeetCode上难度为困难,确实也挺难的,提交了几次才通过,通过的一次还时间空间双吊车尾。。。。。枯了。。。。
还是记录下,后面再优化。
思路在代码的注释上了,直接上代码吧。

代码

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        //总体放向,以nums内的元素做下标,染色下标指向的位置,再遍历nums,判断某位置是否被染色/标记来倒推元素是否存在
        //需要处理的问题:
        //问题1:题目求最小正数,最小正数为1,下标最低只能标记到1,显然,会漏掉0位置,因此以(元素 -1)做下标更为合理
        //问题2:由于nums取值范不再被限定,正负数均有,需要想办法区分,nums内某位置的值是被染色/标记,还是它本来就是个负数。由于需要寻找的是个正数,可以预处理一遍nums,想办法将负数去掉:
        //      1.直接去掉:将nums中的负数全部erase掉,但这样性能太差,循环erase时间复杂度也不满足O(n)
        //      2.将负数置0:元素为0,得到下标0 - 1,即-1,染色/标记毫无意义,直接跳过,所以置0不影响结果,因此选择置0
        int iSize = static_cast<int>(nums.size());
        for(auto& elem : nums){     //预处理
            if(elem < 0){
                elem = 0;
                continue;
            }
            if(elem > iSize){  //需要找的是最小正数,且下标为元素 - 1,所以大于nums长度的直接可以不用考虑了,也置0
                elem = 0;
                continue;
            }
        }
        //以elem - 1 做index,去染色/标记index所指位置,染色/标记方式为将该位置的值改为负数
        int iIndex = -1;
        for(const auto& elem : nums){
            if(0 == elem){                   //index = 0 - 1,即-1,标记-1位置毫无意义:1.会越界;2.求得是正数,0不是正数
                continue;
            }else if(-(iSize + 1) == elem){  //特殊标记位置,跳过
                continue;
            }

            iIndex = std::abs(elem) - 1;     //elem所处位置可能被染色,abs处理来得到原值     
            if(nums[iIndex] < 0){            //已经染色的位置,不用再次染色,避免染色失效
                continue;
            }
            if(0 == nums[iIndex]){          //>>>>>>>>>>iIndex位置的值为0,0无法使用乘以-1的方式染色,需要特别处理一下,赋值为-(iSize + 1)来染色<<<<<<<<<
                nums[iIndex] = -(iSize + 1);
                continue;
            }
            nums[iIndex] *= -1;             //染色/标记index位置
        }
        int iMaxElem = 0;                   //记录下容器内的最大值
        //从前往后,检查nums,查找未染色位置,首个未染色位置即为结果
        for(iIndex = 0; iIndex < iSize; iIndex++){
            if(nums[iIndex] >= 0){
                return iIndex + 1;          //index为元素 - 1,故所求元素应为index + 1
            }
            iMaxElem = iIndex + 1;
        }
        return iMaxElem + 1;                //运行到这,说明容器内的元素全部能够被标记,那么未出现的值,一定比最大元素还大
    }
};


优化

由于关心的是最小正数,于是在预处理阶段,将负数以及数字0,换为了比nums长度大的数,在染色的循环中,及跳过被预处理的数,又跳过本身就大于nums长度的数。减少了循环内的判断次数。提高了部分性能。

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int iSize = static_cast<int>(nums.size());
        //预处理
        for(auto& elem : nums){
            if(elem <= 0){
                elem = iSize + 1;
            }
        }
        //以elem - 1下标然nums染色
        int iIndex = 0;
        for(const auto& elem : nums){
            iIndex = std::abs(elem) - 1;
            if(iIndex >= iSize){        
                continue;
            }
            if(nums[iIndex] < 0){
                continue;
            }
            nums[iIndex] *= -1;
        }
        //寻找未染色位置
        for(int i = 0; i < iSize; i++){
            if(nums[i] > 0){
                return i + 1;
            }
        }
        return iSize + 1;   
    }
};


枯了,还是吊车尾。。。。





原创不易,转载请注明出处,谢谢
原文地址:https://www.cnblogs.com/Keeping-Fit/p/14800423.html