缺失的第一个正整数

继续每日一题

今天给大家带来一道将数组视为哈希表的算法

题目描述:

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

题目示例:
在这里插入图片描述

由于题目要求我们「只能使用常数级别的空间」,而要找的数一定在 [1, N + 1] 左闭右闭(这里 N 是数组的长度)这个区间里。因此,我们可以就把原始的数组当做哈希表来使用。事实上,哈希表其实本身也是一个数组;

最小正整数从1开始,那么没有出现的最小正整数一定在1到n+1之间(因为如果1到n都出现了,那么答案就是n+1)

所以我们可以只考虑数组中的1到n之间的数,负数、0和大于n的数可以忽略(因为它们不影响1到n之间的最小缺失正整数)。

考虑到对时间和空间复杂度的要求,我们可以采用原地置换,具体流程通过文字描述不太直观,下面通过一个case给大家描述一下:

数组 [3,4,-1,1]

  • i=0,nums[0]=3,它应该在位置2(即下标2),交换nums[0]和nums[2]:得到[-1,4,3,1]
    然后i=0,此时nums[0]=-1,不在1到4范围内,跳过。

  • i=1,nums[1]=4,它应该在位置3,交换nums[1]和nums[3]:得到[-1,1,3,4]
    然后i=1,此时nums[1]=1,它应该在位置0,但是位置0是-1,交换:得到[1,-1,3,4]
    然后i=1,现在nums[1]=-1,跳过。

  • i=2,nums[2]=3,在位置2(正确),跳过。

  • i=3,nums[3]=4,在位置3(正确),跳过。

然后遍历数组

  • i=0,num[0]=1(正确);
  • i=1,num[1]=-1,但实际上i=1,对应的数是2,所以返回i+1

但是在置换过程中可能会存在当前位置和要置换的位置的数值是相同的,导致死循环,所以在置换之前还需要比较两个值是否相同

核心逻辑如下:

  • 当前元素:currentVal=nums[i]
  • 当前元素需要在的位置:index=currentVal - 1,因为数组下标从0开始,所以需要减一
  • 要置换的元素:nums[index]
while (true) {
                int currentVal = nums[i];
                //当前元素需要置换到该位置,数组下标从0开始,所以需要减一
                int changeIndex = currentVal - 1;
                int needChangeVal = nums[changeIndex];
                if (currentVal < 1 || currentVal > n) {
                    //不在有效范围无需置换
                    break;
                }
                int needChangeVal = nums[changeIndex];
                if (currentVal == needChangeVal) {
                    //两个数字相等,无需置换
                    break;
                }
                nums[i] = needChangeVal;
                nums[changeIndex] = currentVal;
            }

完整代码如下:

    public static int firstMissingPositive(int[] nums) {
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            while (true) {
                int currentVal = nums[i];
                //当前元素需要置换到该位置,数组下标从0开始,所以需要减一
                int changeIndex = currentVal - 1;
                if (currentVal < 1 || currentVal > n) {
                    //不在有效范围无需置换
                    break;
                }
                int needChangeVal = nums[changeIndex];
                if (currentVal == needChangeVal) {
                    //两个数字相等,无需置换
                    break;
                }
                nums[i] = needChangeVal;
                nums[changeIndex] = currentVal;
            }
        }
        for (int i = 0; i < n; i++) {
            int currentVal = nums[i];
            if (currentVal != i + 1) {
                return i + 1;
            }
        }
        //如果都存在则返回 n+1
        return n + 1;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZNineSun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值