这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战
参加更文30天,算是把一件事情坚持了下来,明天最后一天,继续加油吧。
今天继续做leetcode的第41题,难度等级是困难。
题目
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
示例 2:
输入:nums = [3,4,-1,1]
输出:2
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
提示:
1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1
思路
看了题目描述和限制,还真一下子被难住了。
时间复杂度为 O(n)不难,只使用常数级别额外空间不难,但是要同时满足,就是难点所在了。
看了题解,知道了一个新的思路,而且读到思路之后,就算没看后续的具体解法,也挺让人豁然开朗的。
用一句话来描述就是:利用数组本身nums来存储一些内容。有人还给取了一个名字,我觉得挺贴切的,叫做原地哈希。
说一下具体的解法:首先,要找到没出现的最小正整数,那么如果数组的长度是len的话,答案只可能存在[1, len+1]这个区间里面,因为如果数组刚好是1len,那么答案就是len+1,否则,一定是1len里面,缺失的最小的数。然后我们可以这么操作,遍历一次数组,如果数值在[1, len]里面,就把它跟nums[i]-1下标的数做交换,这样,还完之后,数组第1个数是1,第2个数是2,一路找下去,如果第n个不是n,那n就是答案了。这个方法就是把数字都换到自己应该出现的问题上,然后再次遍历,找出不在位置上的第一个数字即可。当然这个解法还有个注意事项,交换之前,先判断一下nums[i]-1下标的数是不是本身已经是nums[i]了,如果已经是了,证明不需要交换了。
交换的步骤可能说的比较抽象,我画了一个图,标识了每一步交换的方法:
最后,再次遍历这个数组,第1个是1,第2个不是2,所以答案就是2了。
Java版本代码
class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
while (nums[i] >= 1 && nums[i] <= len && nums[i] != nums[nums[i]-1]) {
// 交换 nums[i] 和 nums[nums[i]-1]
int k = nums[i]-1;
nums[i] += nums[k];
nums[k] = nums[i] - nums[k];
nums[i] -= nums[k];
}
}
for (int i = 0; i < len; i++) {
if (nums[i] != i+1) {
return i+1;
}
}
return len + 1;
}
}