給你一個未排序的整數陣列 nums
,請你找出其中沒有出現的最小的正整數。
請你實現時間複雜度為 O(n)
並且只使用常數級別額外空間的解決方案。
示例 1:
輸入:nums = [1,2,0]
輸出:3
解釋:範圍 [1,2] 中的數字都在陣列中。
示例 2:
輸入:nums = [3,4,-1,1]
輸出:2
解釋:1 在陣列中,但 2 沒有。
示例 3:
輸入:nums = [7,8,9,11,12]
輸出:1
解釋:最小的正數 1 沒有出現。
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
思路
約束:
- 未排序 要求O(n) 不能使用排序演算法 且對順序有一定要求 考慮雜湊表(集合)
- 常數級別的空間 不能使用雜湊表(集合)
分析:
考慮答案範圍,最小值可能是1,如所有數都大於1;當nums[i]=i+1時,答案取最大值,最大值是nums.size+1。故答案範圍在[1,nums.size+1]。所以只需要從小到大遍歷該範圍,第一個未出現的數字就是答案。
考慮如果範圍內某一數值x出現,則記錄該值出現,自然想到集合或者雜湊
難點:需要雜湊表(集合),但不能使用雜湊表(集合)
解決:
如何記錄?利用nums陣列,[1,nums.size+1] 需要n個位置記錄,所以讓nums[i]記錄i-1是否出現
如果x在範圍裡,則用nums[x-1]儲存該數字,原來的資料怎麼辦?將二者位置交換即可,交換之後,新資料有兩種情況,在範圍內,則需要繼續交換,出現迴圈,考慮該操作有可能不停止嗎,考慮一直在範圍內的情況,也就是換了之後,目標位置符合條件nums[x-1]=x,同時本來的位置數值換過去也符合,也就是nums[i]=x, 出現了重複的數字,需要考慮該情況;不在範圍內則停止處理。
eg: [3,4,1,-1]
i=0
第一次交換 [1,4,3,-1]
交換後1在範圍內,繼續交換[1,4,3,-1] (其實1已經在位置上了,可以省略) 交換後1在範圍內,nums[0]=1,不需要繼續交換
i=1
第一次交換[1,-1,3,4]
i=2
i=3
結果[1,-1,3,4]
遍歷結果 第一個不符合 nums[i]!=i+1的 i+1就是答案
實現:
int firstMissingPositive(vector<int>& nums) {
for(int i=0;i<nums.size();i++) {
//進行雜湊
while(nums[i]>0&&nums[i]<=nums.size()&&nums[i]!=i+1) {
int t = nums[i];
//需要考慮的死迴圈情況
if(nums[i]==nums[t-1])
break;
nums[i]=nums[t-1];
nums[t-1] = t;
}
}
for(int i=0;i<nums.size();i++) {
//找到第一個不符合條件的就是答案
if(nums[i]!=i+1) {
return i+1;
}
}
return nums.size()+1;
}