力扣算法题,找缺失的第一个最小正整数
看似简单但是能够学到一些解题思路
今天继续刷基础算法题,看到这个题目挂着困难的等级,不以为然,这么区区两行字,题意也很好理解,怎么就是困难题了,当我去写代码的时候才体会到官方的良苦用心啊。
题目如下:找到缺失党的最小正整数
一开始,我的思路是:直接遍历【1,len(nums)】中的数字,有嘛就继续遍历,没有嘛,就直接返回,岂不美哉,然后:
for i in range(1,len(nums)+1):
if i in nums:
continue
else:
return i
return len(nums)+1
然后,就
timeout了。问题出在每次都要在 in nums中判断一次,这样虽然是一句执行语句,但相当于判断n次,时间复杂度为O(n^2),显然不符合题意的。
所以,我又想了另外一种办法,先sort排序,然后在判断,这样时间复杂度就为O(n)了。
res_list=[0]
for num in sorted(nums):
if num<=0:
continue
elif num>0 and num==res_list[-1]+1:
res_list.append(num)
del res_list[0]
return res_list[-1]+1
而且,由于在res_list中,我每次都是增加一个,在del一个,也没有占用什么额外的空间,空间复杂度O(1)
这是我自己的解题思路,然后我就去看官方的题解,(大家刷题的时候也可以参考这种方式,先自己思考思路,然后代码实现,最后和官方给出的题解做一个比较,看自己的代码有哪些可以优化的地方或者和官方的题解之间有什么异同)。
官方给出了两个方式,其一是标记法,本意就是在数组本身上面做文章,通过改变数组中数字所代表的含义来找缺失的最小正整数。
第一步:先把小于0的数,转为len(nums)+1,这样后续遍历修改的时候,就不会影响到整个数组。
第二步:把满足在【1,len(nums)】区间中的数字,对应到他本应该在的位置,并做标记,比如数字5,本来应该放在索引为4的位置,那么就将现在索引为4 的位置做一个标记,怎么标记?通过加负号的方式进行标记,因为经过第一步之后,nums中每一个数字都是非负的,跟非负数相区别,标记为负数。这样,只要在区间内的数字,他的索引位置都标记好了。
第三步:找到没有标记的点,没有标记的点,显然就是缺失的最小正整数应该在的索引。比如缺失了3这个数字,那么3本来在索引2上,此时索引2上的值(不管是几)肯定是大于0的,索引2对应缺失值3.返回3
for i in range(len(nums)):
if nums[i]<=0:
nums[i]=len(nums)+1
for i in range(len(nums)):
num=abs(nums[i])
if 1<=num<=len(nums):
nums[num-1]=(-1)*abs(nums[num-1])
for i in range(len(nums)):
if nums[i]>0:
return i+1
return len(nums)+1
第二种是置换法,这个思路就跟我一开始用sort的想法不谋而合了,先把他排序,然后遍历,
for i in range(len(nums)):
while 1<=nums[i]<=len(nums) and nums[i]!=nums[nums[i]-1]:
tmp=nums[i]
nums[i]=nums[tmp-1]
nums[tmp-1]=tmp
for i in range(len(nums)):
if nums[i] != i + 1:
return i + 1
return len(nums) + 1
不过,排序是从小到大将数组排序,但是置换法,是把原来的数组,换成1,2,3,4,5,…len(nums)-1 …的数组,然后返回索引。