又双摸鱼了,一题打卡。
Leetcode 41. 缺失的第一个正数
由于今天是困难题,所以我心安理得一题打卡(并不,只是懒)
题目
给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-missing-positive
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
看着不是很难,我足足做了一下午也没做出来,没看题解之前只能想到在时间为 O(n) 空间为O(n) 或时间为 O(n2) 空间为 O(1) 的方法,即要不然就弄个哈希表把数组中元素都放到里面去查,要不然就排序(前面说错了,排序的话可以是 O(nlogn))然后从头比对。很明显,这两种方法都不满足题目的要求,但是我也很难想出有什么办法能进行优化,该题官方给出两种方法。
方法一:
首先确定一个事情,即无论数组内有什么数,未出现的最小正整数一定不超过数组的长度 N ,因为按照题意数组内必有一个未出现的最小正整数,而一个数组能出现的最小正整数无非是 [1, N] , 故本题的答案在该范围内,那我们只需要查找并标记已出现的 [1, N] 中的数就可以了,因为大于 N 的数所得的答案一定是 N 。好了,怎么标记呢,这里为了不开辟额外空间就要使用原数组,题解给了一个很好的方法:将已出现的位置的值置为负。具体解释是这样的,当你遍历到一个值 x 时,将 nums[x - 1] 的值置为负以标记 x 已被找到,这样最后就可以通过查看哪个值不为负,该值的下标加一即为答案;不过这样做需要处理两个问题,一是遍历时可能把后面的值修改为负值,当遍历到该负值会出错(数组越界),需要遍历时使用其绝对值;二是初始数组可能存在非正整数,对这些数可以将其置为 N + 1,因为 N + 1 不会影响最后结果,这些值本身也不会影响最后结果。最后附上本人的代码:
int firstMissingPositive(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++){
if(nums[i] <= 0){
nums[i] = nums.size() + 1;
}
}
for(int i = 0; i < nums.size(); i++){
if((abs(nums[i]) <= nums.size()) && nums[abs(nums[i]) - 1] > 0){
nums[abs(nums[i]) - 1] = -nums[abs(nums[i]) - 1];
}
}
for(int i = 0; i < nums.size(); i++){
if(nums[i] > 0){
return i + 1;
}
}
return nums.size() + 1;
}
方法二:
方法二我没有写代码,只讲一下思路。思路很简单,置换,既然知道数组中最小可能的值为 [1, N] ,那不如直接按照 [1, N] 对数组排序;遍历数组,对每个值 x 如果在 [1, N] 范围内则交换 x 与 nums[x - 1],最后看哪个数组下标与其值 x - 1 对不上则为答案;需要注意的仅有两数相同时避免进入死循环。