挑战LeetCode热题100(TS版本)打卡第六天

91 阅读4分钟

挑战LeetCode热题100(TS版本)

打卡第六天

普通数组

1.缺失的第一个正数

给你一个未排序的整数数组 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,2,3,4,...,n] 我们很容易知道最小的正整数就是1,但是对于这种数组[4,3,-1,1],当我们把他恢复成这种形式时:[1,-1,3,4] 我们可以很容易的看出缺失的是2。因为如果没有缺失的话他应该是[1,2,3,4];再比如对于[7,8,9,11,12],由于索引0,1,2,3,4 都和数组对应的值都不相同,得出最小正整数是1。
  • 那么如何恢复成原数组呢。当我们我们遍历数组时,当前被遍历的x(x <= n)值他的位置应该在原数组的 x - 1的位置。也就是说nums[nums[i] - 1] = nums[i]。但是为了避免交换位置后出现相邻两个都相等,陷入死循环,还要提前判断nums[nums[i] - 1] = nums[i],并跳出循环。
    function firstMissingPositive(nums: number[]): number {
    /**
        思路:置换法:答案数组应该在[1,2,3...,n]中,如果遍历的当前值x在[1,n],那么他的位置应该是x-1,即nums[i]= x - 1;nums[i] = nums[nums[i] -1]  nums[nums[i] -1]  = nums[i];被置换后如果当前值不是数组下标i+1,那么最小值就是i+1;如果全是那么则是n+1
     */
    const n = nums.length
    // for(let i=0;i<n;i++) {
    //     while(nums[i] >=1 && nums[i] <= n && nums[nums[i] - 1] !== nums[i]) {
    //         [nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]]
    //     }
    // }

    // for(let i=0;i<n;i++) {
    //     if(nums[i] !== i+1) {
    //         return i+1
    //     }
    // }
    
    // return n+1
};

思路二:哈希表;

  • 正常情况把数组中每一项都存到hash中,然后从整数1开始遍历累加,如果后续存在有整数不在hash中,该值就是最小的正整数
  • 但是采用上述的办法有问题就是如果题目要求不可以使用额外的空间,那怎么办呢?其实我们可以把原数组改造成类似于hash表的访问方式;
  • 我们可以给每个元素(x<=n && x > 0)做个标记,比如第i元素在正常数组中的位置应该是 num[i] - 1,那我们就把对应的位置给他打卡标记,这个标记我们可以采用负数来标记。但是有个问题是如果数组中原本就存在负数或0应该怎么办呢?其实我们可以把原来的负数0统一的变成n+1。这样就可以把原来的负数和0给排除掉。当遍历完数组后,我们只需要查看最近的一个没有被打标记(正数)在什么位置,当前这个位置的索引i+1就是最小的正整数;反之如果都被打上了标记那最小正整数在n+1的位置;
   function firstMissingPositive(nums: number[]): number {
       // 使用额外的hash
     //   const map = new Set()
    //    for(let i=0;i<nums.length;i++) {
    //        map.set(nums[i])
    //    }

    //    for(let i=1;i<=Max;i++) {
    //        if(!map.has(i)) {
    //            return i
    //        }
    //    }
       
       // 把数组当成hash
       const n = nums.length
       for(let i=0;i<nums.length;i++) {
           if(nums[i] <=0) {
                 nums[i] = n + 1
           }
       }
       
       for(let i=0;i<nums.length;i++) {
           const num = Math.abs(nums[i])
           if(num <=n) {
                nums[num - 1] = -Math.abs(nums[num - 1])
           }
       }
       
       for(let i=0;i<nums.length;i++) {
           if(nums[i] > 0) {
               return i + 1
           }
       }
       
       return n + 1
   }

总结

  • 求数组缺失的正数算是LeetCode 比较困难的题型了。解题思路hash可能是大家容易想到的吧!但是如果题目约束空间复杂度在O(n),只能借助原数组作为hash(如果可以改变原数组)。另外置换的思路也比较容易理解,核心是数组的nums[i] 在原数组中正确的位置应该是num[num[i] - 1],如果把这些对应的位置都填满了,只需要判断哪些是和下标没有对应关系的最近一个就是答案。
  • 如有更好的答案,欢迎评论!如有错误欢迎评论