Leetcode之41.缺失的第一个正数

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode之41.缺失的第一个正数相关的知识,希望对你有一定的参考价值。

题目

题目描述

给你一个未排序的整数数组 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

前面三种解法都不满足题目要求,只是提供一种思路。

解法1

class Solution {
    /**
     * <p>思路:将数组排序后,保存数组中的正整数到集合中,再通过集合中相邻元素之差是否为1来判断缺失的正数。</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,将数组排序</li>
     *     <li>第二步,将数组中的正整数添加到集合中(不包括0)</li>
     *     <li>第三步,判断相邻元素差是否为1,如果不为1,那么表示list.get(i)+1不存在</li>
     * </ul>
     * <p>结果:成功(但不满足时间复杂度为O(n)和常数级别的额外空间)</p>
     * <ul>
     *     <li>执行用时:9 ms ,在所有Java提交中击败了 12.89%的用户</li>
     *     <li>内存消耗:97.7 MB ,在所有Java提交中击败了 9.76%的用户</li>
     *     <li>通i过测试用例:171/ 171</li>
     * </ul>
     *
     * @param nums 未排序的整数数组
     * @return 数组中没有出现的最小的正整数
     */
    public int firstMissingPositive(int[] nums) {
        // 第一步,将数组排序
        Arrays.sort(nums);
        // 第二步,将数组中的正整数添加到集合中(不包括0)
        List<Integer> list = new ArrayList<>();
        for (int num : nums) {
            if (num > 0) {
                list.add(num);
            }
        }
        // 如果集合为空表示全是负数则返回1,或者集合中的第一个元素大于1那么缺失的第一个正数一定是1
        if (list.size() == 0 || list.get(0) > 1) {
            return 1;
        }
        // 第三步,判断相邻元素差是否为1,如果不为1,那么表示list.get(i)+1不存在
        for (int i = 0; i < list.size() - 1; i++) {
            if (list.get(i + 1) - list.get(i) > 1) {
                return list.get(i) + 1;
            }
        }
        // 还可能存在一种情况,例如[0,1,2],那么缺失的就是3,即为集合中的最后一个元素的值加1
        return list.get(list.size() - 1) + 1;
    }
}

解法2

class Solution {
    /**
     * <p>思路:将数组中所有正整数保存到Map集合中,然后遍历1到max之间的所有数判断是否在Map集合中出现,如果没有出现则表示缺失。</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,将数组中的正整数保存到Map集合中,并且求得数组中的最大值</li>
     *     <li>第二步,从1到最大数开始遍历,如果该数没有出现在Map集合中,表示缺失</li>
     * </ul>
     * <p>结果:成功(但不满足常数级别的额外空间)</p>
     * <ul>
     *     <li>执行用时:26 ms ,在所有Java提交中击败了7.30%的用户</li>
     *     <li>内存消耗:106.6 MB ,在所有Java提交中击败了 5.04%的用户</li>
     *     <li>通i过测试用例:171/ 171</li>
     * </ul>
     *
     * @param nums 未排序的整数数组
     * @return 数组中没有出现的最小的正整数
     */
    public int firstMissingPositive(int[] nums) {
        // 第一步,将数组中的正整数保存到Map集合中,并且求得数组中的最大值
        Map<Integer, Integer> map = new HashMap<>();
        int max = nums[0];
        for (int num : nums) {
            if (num > 0) {
                map.put(num, num);
            }
            max = Math.max(max, num);
        }
        // 如果最大值为负数表示全是负数则返回1,或者集合为空表示全是负数返回1
        if (max < 0 || map.size() == 0) {
            return 1;
        }
        // 第二步,从1到最大数开始遍历,如果该数没有出现在Map集合中,表示缺失
        for (int i = 1; i < max; i++) {
            if (!map.containsKey(i)) {
                return i;
            }
        }
        return max + 1;
    }
}

解法3

class Solution {

    /**
     * <p>思路:数组中每个元素都可以存放到新数组中对应下标位置中,循环新数组,如果新数组中有元素值为0则表示该元素缺失,对应的下标就是缺失的值。</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,创建长度为数组长度加1的数组,将数组中每个数放到新数组对应的下标位置中,而负数和大于等于数组长度的数抛弃掉</li>
     *     <li>第二步,遍历新数组,如果某个位置的值为0则表示该处值缺失,即对应的下标就是缺失的值</li>
     * </ul>
     * <p>结果:成功(但不满足常数级别的额外空间)</p>
     * <ul>
     *     <li>执行用时:2 ms ,在所有Java提交中击败了91.00%的用户</li>
     *     <li>内存消耗:92.4 MB ,在所有Java提交中击败了73.12%的用户</li>
     *     <li>通i过测试用例:171/ 171</li>
     * </ul>
     *
     * @param nums 未排序的整数数组
     * @return 数组中没有出现的最小的正整数
     */
    public int firstMissingPositive(int[] nums) {
        // 第一步,创建长度为数组长度加1的数组,将数组中每个数放到新数组对应的下标位置中,而负数和大于等于数组长度的数抛弃掉
        int[] newNums = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < 0 || (nums[i] >= nums.length && nums[i] != nums.length)) {
                continue;
            } else {
                newNums[nums[i]] = nums[i];
            }
        }
        // 第二步,遍历新数组,如果某个位置的值为0则表示该处值缺失,即对应的下标就是缺失的值
        for (int i = 1; i < newNums.length; i++) {
            if (newNums[i] == 0) {
                return i;
            }
        }
        return nums.length + 1;
    }
}

解法4

public class Solution {
    /**
     * <p>思路:原地哈希,数组中所有小于等于数组长度的数一定能在数组中找到它对应的位置,然后进行标记,再次遍历数组即可找到结果。参考官网题解:https://leetcode-cn.com/problems/first-missing-positive/solution/que-shi-de-di-yi-ge-zheng-shu-by-leetcode-solution/</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,将数组中所有的负数和零修改为nums.length+1</li>
     *     <li>第二步,遍历数组对每个小于等于数组长度的数进行标记,即将遍历到的数x存储到x-1的下标位置处,并且打上负数标记,表示这个数存在于数组中</li>
     *     <li>第三步,遍历数组,找到的第一个正数即为缺失的数</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *    <li>执行用时:2 ms, 在所有 Java 提交中击败了91.56% 的用户</li>
     *    <li>内存消耗:94.7 MB, 在所有 Java 提交中击败了35.06% 的用户</li>
     *    <li>通过测试用例:171 / 171</li>
     * </ul>
     *
     * @param nums 未排序的整数数组
     * @return 数组中没有出现的最小的正整数
     */
    public int firstMissingPositive(int[] nums) {
        // 第一步,将数组中所有的负数和零修改为nums.length+1
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            if (nums[i] <= 0) {
                nums[i] = len + 1;
            }
        }

        // 第二步,遍历数组对每个小于等于数组长度的数进行标记,即打上负数标记,表示这个数存在于数组中
        for (int i = 0; i < len; i++) {
            // 因为有些数可能已经被标记了
            int num = Math.abs(nums[i]);
            // 对数进行标记
            if (num <= len) {
                nums[num - 1] = -Math.abs(nums[num - 1]);
            }
        }

        // 第三步,遍历数组,找到的第一个正数对应的下标加1就是结果
        for (int i = 0; i < len; i++) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }
        return len + 1;
    }
}

解法5

public class Solution {
    /**
     * <p>思路:数组置换元素。参考官网题解:https://leetcode-cn.com/problems/first-missing-positive/solution/que-shi-de-di-yi-ge-zheng-shu-by-leetcode-solution/</p>
     * <p>步骤:</p>
     * <ul>
     *     <li>第一步,交换数组中的nums[nums[i]-1]与nums[i]</li>
     *     <li>第二步,遍历数组,找到nums[i]不等于i+1的值,返回i+1</li>
     * </ul>
     * <p>结果:成功</p>
     * <ul>
     *     <li>执行用时:2 ms, 在所有 Java 提交中击败了91.56% 的用户</li>
     *     <li>内存消耗:94.7 MB, 在所有 Java 提交中击败了36.24% 的用户</li>
     *     <li>通过测试用例:171 / 171</li>
     * </ul>
     *
     * @param nums 未排序的整数数组
     * @return 数组中没有出现的最小的正整数
     */
    public int firstMissingPositive(int[] nums) {
        int len = nums.length;

        // 第一步,交换数组中的nums[nums[i]-1]与nums[i]
        for (int i = 0; i < len; i++) {
            while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
                // 交换nums[nums[i]-1]与nums[i]
                int temp = nums[nums[i] - 1];
                nums[nums[i] - 1] = nums[i];
                nums[i] = temp;
            }
        }

        // 第二步,遍历数组,找到nums[i]不等于i+1的值,返回i+1
        for (int i = 0; i < len; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        return len + 1;
    }
}

以上是关于Leetcode之41.缺失的第一个正数的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode:缺失的第一个正数41

[LeetCode 41.] 缺失的第一个正数

LeetCode 41. 缺失的第一个正数 | Python

[leetcode] 41. 缺失的第一个正数

leetcode 41 缺失的第一个正数

leetcode——41. 缺失的第一个正数