输入:一个没有排序的正数数组nums
输出:在 nums数组中未出现的最小的正整数
规则:数组中的元素可能是负数,也可能重复。要求时间复杂度O(n),空间复杂度O(1)。
分析:题目其实很简单,遍历一次放入map中。然后从1到n遍历,是否在map中存在。但是这样不符合空间复杂度。
分析2:力扣官方解答。首先我们不需要考虑负数、0,以及大于n+1的值。因为答案一定在[1,n+1]之间。因为空间限制条件,我们可以使用原数组实现一个map,类似布隆过滤器。我们假设修改之后nums[i]<0,表示i+1存在;否则i+1不存在。
具体实现算法是:
1 检查数组中1是否存在。不存在则返回1,否则进入2.
2 将数组中不在[1,n]之间的值都设置为1。
3 遍历数组,设置nums[nums[i] -1] = 负数,这里需要注意,元素值可能重复。
4 遍历数组,找到第一个nums[i]<0的元素返回。
说明:代码实现一些细节和算法过程略有不同。
public int firstMissingPositive(int[] nums) {
int n = nums.length;
//检查1是否缺失
int oneCount = 0;
for(int num : nums){
if(num==1){
oneCount =1 ;
break;
}
}
if(oneCount == 0) return 1;
if(n == 1) return 2;
//替换无效数字为1
for(int i=0; i < n; i++){
if(nums[i] <=0 || nums[i]>n){
nums[i] = 1;
}
}
//一个布隆过滤器 nums[i] >0表示 i不存在;nums[i]<0,表示i存在。nums[0]代表n是否存在。
for(int i=0;i <n; i++){
int a = Math.abs(nums[i]);
if(a == n){
nums[0] = - Math.abs(nums[0]);
}else{
nums[a] = -Math.abs(nums[a]);
}
}
for(int i=1;i <n; i++){
if(nums[i] > 0){
return i;
}
}
return nums[0] >0? n: n+1;
}
分析3:其实也是用nums[i]的值表示i+1是不是存在。这里的判断依据是如果nums[i]=i+1,则说明i+1存在,否则不存在。这个方法实现的技巧是交换。上一个方法实现的技巧是替换。原链接。
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for(int i=0;i <n; i++){
while(nums[i] >0 && nums[i]<=n && nums[nums[i] -1 ]!=nums[i]){
swap(nums,i,nums[i]-1);
}
}
for(int i=0;i <n; i++){
if(nums[i] != i+1){
return i+1;
}
}
return n+1;
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i] ;
nums[i] = nums[j];
nums[j] = tmp;
}