问题描述
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
要求:
1
<
=
n
u
m
s
.
l
e
n
g
t
h
<
=
5
∗
1
0
5
1 <= nums.length <= 5 * 10^{5}
1<=nums.length<=5∗105
−
2
31
<
=
n
u
m
s
[
i
]
<
=
2
31
−
1
-2^{31} <= nums[i] <= 2^{31} - 1
−231<=nums[i]<=231−1
算法说明
本题主要的思路为:以位置作为正整数的代表做标记。
假如没有时间复杂度和空间复杂度的限制,我们的方法就多了:
第一种方法,开辟一个标记数组label,将label的下标0~nums.length 与 nums当中的整数对应起来。
如果1=<nums[i]<=nums.length,就将对应的label[nums[i] -1] 设置为true,反之就为false。
之后再判断label中的数是否全为true,如果是,则返回nums.length + 1
(因为label中的值全为true的话,代表从1到nums.length的整数全部都在nums当中,也就是说nums中出现了从1到nums.length所有的正整数,那么没有出现的最小的正整数自然就是nums.length + 1了 )
第二种方法,排序,将排序后的值依然放在nums中,然后设置min1和min2两个数。
如果nums.length = 1就根据nums[0]的数判断就行: nums[0] = 1则返回2,nums[0]等于其他值则返回1;
如果nums.length > 1就根据初始化min1 = nums[0] , min2 = nums[[1]]。
如果min2 -min1 > 1,则返回min1 +1;如果min2 - min1 = 1 则将min2,min1都往后移动一位,再进行下一次判断;如果当min2 = nums的最后一个元素时,min2 - min1 = 1,则返回nums.length + 1。
而现在我们有了时间复杂度和空间复杂度的限制,因此我们采用第一种方法,并将其稍作修改.
注意,这里默认nums中的数是可以修改的。如果nums中的数是固定值,那么不存在在该时间空间复杂度限制条件下的算法。
在此方法中,由于不能开辟新的O(n)数组,我们的label数组自然而然地就变成了nums。那么具体如何操作呢?
首先,进行这样一个操作:如果nums[i] <0,那么nums[i] = nums.length+1。
接着,进行以下循环:i属于[0,nums.length -1],如果nums[i]的绝对值大于等于1且小于等于numslength + 1,则nums[ | nums[i] - 1 | ] = -nums[ | nums[i] - 1 | ] ;如果为其他情况,则进行下一步循环
接着,进行以下的判断:如果nums中的数全都为负数,则返回nums.length + 1;否则,返回第一个使得nums[i] > 0 的 i。
实际代码
实现该算法的python3代码如下:
def firstMissingPositive(self, nums: list[int]) -> int:
size = len(nums)
for i in range(0,size):
if nums[i] <= 0:
nums[i] = size + 1
for i in range(0,size):
if (nums[i] <= size and nums[i] >=1) or ( -nums[i] <= size and -nums[i] >= 1):
if nums[i] > 0:
index = nums[i] - 1
if nums[index] > 0:
nums[index] = -nums[index]
else:
index = -nums[i] - 1
if nums[index] > 0:
nums[index] = -nums[index]
else:
continue
flag = 1
for i in range(0,size):
if nums[i] > 0:
flag = 0
if flag == 1:
return size + 1
else:
for i in range(0,size):
if nums[i] >= 0:
return i+1