手撕算法——缺失的第一个整数

题目描述

给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。

示例 1:

输入: [1,2,0]
输出: 3

示例 2:

输入: [3,4,-1,1]
输出: 2

示例 3:

输入: [7,8,9,11,12]
输出: 1

提示:
你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间

思路分析

思路一:先排序在寻找

如果这道题没有没有时间复杂度和空间复杂度的要求,相信大家能很轻松的写出来,比如直接对数组进行排序,然后比较得到第一个缺失的数。代码如下:

class Solution {
    public int firstMissingPositive(int[] nums) {
        if(null==nums||nums.length<=0){
            return 1;
        }

        Arrays.sort(nums);
        int num=1;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==num){
                num++;
            }
        }
        return num;
    }
}

以上代码也可以通过,接下来我们分析时间复杂度和空间复杂度。
时间复杂度: 因为这里用到了JDK提供的Arrays.sort(int[] nums)进行排序,JDK1.8是这样描述的

排序算法是由Vladimir Yaroslavskiy,Jon Bentley和Joshua Bloch提供的双轴快速排序。 该算法在许多数据集上提供O(n log(n))性能,导致其他快速排序降级为二次性能,并且通常比传统(单轴)Quicksort实现更快。

因此该题目的时间复杂度为 O(nlog(n)).
空间复杂度: 这里并没有额外的创建数组,因此空间复杂度为O(1)。
可以得出结论,空间复杂度符合题目要求,时间复杂度不符合题目的要求!!

思路二:将数组索引作为哈希表

在使用该思路求解这道题目之前,我们可以先看一道简单题
268. 缺失数字
题目描述:
给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。

示例 1:

输入: [3,0,1]
输出: 2

示例 2:

输入: [9,6,4,2,3,5,7,0,1]
输出: 8

这道题目相对来说比较简单,数字序列是从0到n,因此我们只需要将每个数字放置它对应的下标处,然后遍历,如果 i != nums[i] 则返回结果。代码如下:

class Solution {
    public int missingNumber(int[] nums) {
        if(nums==null||nums.length<=0){
            return 0;
        }
        int len=nums.length;
        for(int i=0;i<len;i++){
            while(nums[i]>=0&&nums[i]<len&&nums[i]!=i){
                //将下标i处的值nums[i]放到下标为nums[i]处
                swap(nums,i,nums[i]);
            }
        }
        
        for(int i=0;i<len;i++){
            if(nums[i]!=i){
                return i;
            }
        }

        return len;
    }
    public void swap(int[] nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }
}

现在我们回到这道题目,确实的第一个整数,与268题缺失数字的不同之处在于数字序列中有了负数,同时从1开始计数而不是从0。即下标 i 处存放的的值应为i+1,也即num[i]应该存放在下标 nums[i]-1处。

class Solution {
    public int firstMissingPositive(int[] nums) {
           if(nums==null||nums.length<0){
            return 1;
        }

        int len=nums.length;
        for(int i=0;i<len;i++){
            //0 1 2-> 1 2 0
            while(nums[i]>0&&nums[i]<=len&&nums[i]!=nums[nums[i]-1]){
                //将下标i处的值应该存放的数值为 i+1
                //也即num[i]存放在下标nums[i]-1处
                swap(nums,i,nums[i]-1);
            }
        }

        for(int i=0;i<len;i++){
            if(nums[i]!=i+1){
                return i+1;
            }
        }

        return len+1;
    }
    public void swap(int[] nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }    
}

时间复杂度: 两次for循环解决问题,因此时间复杂度为n。
空间复杂度: 并没有额外的开辟数组空间,因此空间复杂度为O(1).
符合题目的要求!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值