First Missing Positive

First Missing Positive

题目

题目链接:https://leetcode.com/problems/first-missing-positive

Given an unsorted integer array, find the smallest missing positive integer.

Example 1:

Input: [1,2,0]
Output: 3

Example 2:

Input: [3,4,-1,1]
Output: 2

Example 3:

Input: [7,8,9,11,12]
Output: 1

Note:
Your algorithm should run in O ( n ) O(n) O(n) time and uses constant extra space.

分析

给出一个无序的int类型数组,要求出其缺失的最小正整数。要求时间复杂度为 O ( n ) O(n) O(n)。并且需要的额外空间只能是常量大小。
如果没有后面的要求,这道题还是很简单的,但是有了后面时间复杂度和空间复杂度的要求,难度就变为hard了。

不符合要求的解法:

  • 使用普通的排序,然后从头到尾遍历即可。此时时间复杂度为 O ( n ∗ l o g n ) O(n*logn) O(nlogn)
  • 由于是int类型,我们可以使用桶排序的方式,虽然此时时间复杂度为 O ( n ) O(n) O(n),但额外空间过大。

正确的解法:
在常量大小的额外空间时,排序算法的时间复杂度最小为 O ( n ∗ l o g n ) O(n*logn) O(nlogn),因此,不能使用排序算法。
要达到时间复杂度为 O ( n ) O(n) O(n)的要求,这意味着我们只能对该数组进行少量次数的遍历。

注意到题目要求的是找出缺失的最小正整数。假如没有重复的数,并且数组已经排好序了。如果没有缺失的数,那么第i个元素的值应该是i + 1(因为0不属于正整数);那么最小缺失的正整数,应该是第一个nums[i]不等于i + 1本来应该的值,即i + 1。如果没有缺失,那么就是nums.length + 1
因此,我们只需要遍历数组,将每个元素的值放置到正确的位置。然后再遍历一次,找出第一个不符合要求的位置即可。

那么,对于不符合要求的数应该放置再哪里呢?注意到一个事实,如果第i个元素不符合要求,那么之后的元素如何放置都没有关系(因为我们找的是第一个不符合要求的)。

所以我们可以从右到左遍历整个数组,对于第j个元素

  • 如果当前元素的值小于等于0(要的是正整数)或者是当前元素的值大于j + 1,则说明当前元素不符合要求,继续向左遍历(j--)。
  • 如果当前元素的值等于j + 1,说明该位置放对了,不需要改变,也是继续向左遍历。
  • 如果当前元素的值temp大于0并且小于j + 1。那么他应该被放置到nums[temp - 1]的位置,然后将当前元素的值置为nums[temp - 1]原本的值。如果考虑到有重复的元素,即nums[temp - 1]原来的值为temp(已经正确放置),此时也不需要改变当前元素的值,继续向左遍历即可。
  • 遍历完成后,重新遍历一遍,找出第一个nums[j] != j + 1的位置,然后返回j + 1即可。如果都符合,返回nums.length + 1

对于这种解法,总共需要遍历两遍,在第一次遍历的时候,有可能对于某个位置j,需要替换前面所有的元素(替换j - 1次,最差情况,这时总共需要遍历3遍,但时间复杂度也是 O ( n ) O(n) O(n))。时间复杂度为 O ( n ) O(n) O(n)。替换时均在原来的数组上进行,空间复杂度为 O ( 1 ) O(1) O(1)。符合题目要求。

代码如下

class Solution {
    public int firstMissingPositive(int[] nums) {
    	int temp = 0;
    	int j = nums.length - 1;
    	while (j >= 0) {
    		temp = nums[j];
    		// 如果没有缺失,第 j 个位置的值应该是 j + 1
			if (temp <= 0 || temp >= j + 1) {  // 该位置的值符合要求或者是超出范围
				j--;
			} else if (nums[temp - 1] == temp) {  // 有重复元素
				j--;
			} else {  // 将 nums[j] 放到正确的位置
				nums[j] = nums[temp - 1];
				nums[temp - 1] = temp;
			}
			
		}
		// 寻找不符合要求的位置
		for (j = 0; j < nums.length; j++) {
			if (nums[j] != j + 1) {
				return j + 1;
			}
		}
		return j + 1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值