输入:一个没有排序的正数数组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;//替换无效数字为1for(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;}