LeetCode解题 41:First Missing Positive
Problem 41: First Missing Positive [Hard]
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) time and uses constant extra space.
来源:LeetCode
解题思路
基本思想为哈希表,将数组的值与坐标联系起来,第一遍循环时根据值nums[i]
用一定规律改变该坐标指向的值nums[nums[i]]
,第二遍循环时根据上述规律找出未被改变的坐标,即为第一个缺失的正数。
难点:
- 由于题目中要求只能使用常数级别的额外空间,因此不能创建新数组newnums[N],使用newnums[nums[i]]=1的方法记录nums中的值,只能使用in-place的方法在原数组nums上修改,同时不能改变原本的信息。
【解决方法】: 通过改变nums[nums[i]]
的符号,来记录数组中的值nums[i]
。 nums[i]
的值有可能不在数组的坐标范围内。
【解决方法】: 假设数组长度为N,则第一个缺失的正数不可能大于N+1,因此数组中小于等于0和大于N的值都可以忽略,只关注[1, N]范围内的数。
具体思路:
- 首先遍历数组,记录数组中是否含有1,并将非正数和大于N的值改为1。假如原本不含1,则1就是第一个缺失的正数,可以直接返回。
- 第二次遍历数组,此时数组中的值的范围应该是[1, N],遍历至
nums[i]
时,取其绝对值作为坐标pos
,将nums[pos-1]
的值变为负数,若已经是负数则保持负数,这一步将数组中的值[1, N]映射到相应坐标[0, N-1]上。
例如:nums[i] = 5,则将nums[4]变为-|nums[4]|。 - 第三次遍历数组,检查
nums[i]
的符号,若为正数,说明数组中没有出现值i+1
,此时直接返回i+1
。 - 如果
nums[i]
都为负数,说明整数1~N都出现过,则应返回N+1。
整个算法一共遍历了三次数组,时间复杂度为O(n),空间复杂度为O(1)。
要点:哈希表
、in-place
Solution (Java)
class Solution {
public int firstMissingPositive(int[] nums) {
int N = nums.length;
int min = 0;
for(int i = 0; i < N; i++){
if(nums[i] == 1){
min = 1;
}
else if(nums[i] < 1 || nums[i] > N){
nums[i] = 1;
}
}
if(min == 0) return 1;
int pos;
for(int i = 0; i < N; i++){
pos = Math.abs(nums[i]);
nums[pos-1] = - Math.abs(nums[pos-1]);
}
for(int i = 0; i < N; i++){
if(nums[i] > 0) return i+1;
}
return N+1;
}
}
修改过程
- 一般的哈希表思路需要另外创建一个长度为N的数组,不符合只能使用常数级别空间的题意,因此需要用in-place的想法,利用符号达到哈希表效果。