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

图片.png

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

图片.png

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值