缺失的第一个正数

54 阅读3分钟

纪念一道仅仅看了几秒思路,然后手撸就通过率达到89%的hard难度字符串题,虽死活A不出来,但本蒟蒻还是有小小成就感的。

所以心血来潮,现附上自己的错误代码和正确解,以及学习总结。

思路:
  • 第一个关键:这个最小确缺失整数一定在区间1~数组长度+1这个区间内(可以举例模拟)。
  • 由于题目限制了时间复杂度O(n),空间复杂度为常数,故交换数值位置的方法较为合适。
  • 根据数组的元素值放到适合它的下标处,即 元素i放在第i个位置。
  • 而对于超过长度+1范围的数值 则不予理会,跳过
  • 遍历数组,对于完成交换的数组,首个值与下标不对应的元素 就是我们要找的最小缺失值
  • 若发现所有数组元素的值和下标是对应的,此时最小的值就是nums.length+1
var firstMissingPositive = function(nums) {
    n = nums.length;
    if (n==1) {
        if (nums[0]>1||nums[0]<=0) {
            return 1
        }
        return 2
    }
    
    //如果元素值在1-n之间,并且当前元素的值和下标不对应 需要交换的数字的下标不在正确的位置上 执行替换
    for (i=0;i<=n;i++) {
        if (nums[i]>=0 && nums[i]<=nums.length && nums[i]!==i) { //值为长度+1可以不用调换了
            // console.log(nums[i],nums[nums[i]]);
            [ nums[nums[i]], nums[i] ]=[ nums[i], nums[nums[i]] ];
            if (nums[i]>=0 && nums[i]<=nums.length && nums[i]!==i) {
                [ nums[nums[i]], nums[i] ]=[ nums[i], nums[nums[i]] ];
            }
            console.log(nums);
        }
    }
    
    
    for (j=1;j<=n;j++) {
        if ( nums[j]!==j) {
            console.log('此处不等',j)
            return j
        }
    }
    return n+1
};

自己的错误,在于

  1. 没有在每次循环中首先取当前i下标的元素出来判断是否需要交换,因为有可能一次交换后又产生了一个新的需要交换的元素,需要继续判断。

  2. 且第一个交换位置的for循环中将i++不能设在for条件中,这样会导致i直接自增从而忽略了刚才那次交换后可能产生的新的需要交换的元素。

  3. 判断交换条件的数据结构:对于每个元素,都要满足自身-1==下标,故正确的判断是取出每个值index,只要不是其下标不是index-1就要替换,nums[index-1]!==index。 自己错写成了nums[i]!==i, 和index-1!==i(这个判断看似可以,但遇到[1,1]这样包含相同元素的输入则会陷入交换后依然等价的死循环)

得出正解

var firstMissingPositive = function(nums) {
    let n  = nums.length;

    for(let i = 0;i<n;){
        let index = nums[i]; 
         //如果元素值在1-n之间,并且当前元素的值和下标不对应 执行替换

        if(index>0 && index<=n && index!==nums[index-1]){ //要求每个数(index) 减1 即为它的下标
            //console.log(i,index-1,nums)
            swip(nums,i,index-1)
            //console.log(index,nums)
        }else {
            i++
            //console.log('i加至',i)
        }
    }
    
    for(let i=0;i<n;i++){
        // 下标没有对应上,直接返回i+1为最小正整数
        if(i+1!==nums[i]){
            return i+1;
        }
    }
    // 都对应上了最小正整数就是数组长度+1 
    return n+1;
    
    function swip(nums,i,j){
        let temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
};
总结:

桶排序思路,把nums数组有效范围内的元素放到对应下标位置上,首个不符合的就是缺失值,若都符合则是length+1。