一、问题描述
给你一个未排序的整数数组 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 没有出现。
自用示例 4:
输入: nums = [0,2,2,1,1]
输出: 3
自用示例 5:
输入: nums = [0]
输出: 1
自用示例 6:
输入: nums = [-1,-2,-3]
输出: 1
自用示例 7:
输入: nums = [1,2,3]
输出: 4
提示:
● n == nums.length
● 1 <= n <=
1
0
5
10^5
105
● 1 <= nums[i] <= n
三、代码
废弃方法一:二重循环
//从1开始依次枚举正整数至 n+1,并遍历数组,判断其是否在数组中。
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for (int i = 1; i <= n + 1; i++) {
//初始化置为false,当遍历匹配到时,置为ture
bool found = false;
for (int j = 0; j < n; j++) {
if(nums[j] == i) {
found = true;
break;
}
}
//返回标记false的值,即未匹配到的正整数
if (!found) {
return i;
}
}
return 0;
}
};
废弃方法二:哈希表
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
unordered_map<int, int> mp;
int n = nums.size();
//记录每个数出现的次数
for (auto& num : nums) {
mp[num]++;
}
//遍历1到n,进行匹配比较
for (int i = 1; i <= n; i++) {
int count = mp[i];
/*如果键 i 不存在于 mp 中,那么 mp[i] 会被初始化为 int 的默认值,即 0。
这是因为 unordered_map 通过其 operator[] 为不存在的键隐式地插入了一个默认构造的值。
所以,即使键 i 没有被显式地添加到 mp 中,mp[i] 仍然可以安全地访问,并且会返回 0。*/
//如果次数为0,说明为缺失的第一个正整数,将其返回
if (mp[i] == 0) {
return i;
}
/* count方法
if (!mp.count(i)) {
return i;
} */
/* find方法
if (mp.find(i) == mp.end()) {
return i;
} */
}
return n + 1;
}
};
方法一:置换
//将范围正确的数,即[1, n]的数x放置到对应的下标位置,即x - 1
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n; i++) {
//若元素范围在[1, n]中,且未被置换到相应位置,进行交换
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
swap(nums[nums[i] - 1], nums[i]);
}
}
//如果匹配不相等,则该位置所代表的数缺失,否则说明缺失n + 1
for (int i = 0; i < n; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
return n + 1;
}
};
方法二:负号标记
//将范围不正确的数,即非正数置为n + 1,将范围正确的数置为负,然后遍历判断是否为负,如果为负,代表这个数已存在,若为正,则代表找到了缺失的数,否则返回n + 1
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for (int& num : nums) {
if (num <= 0) {
num = n + 1;
}
}
for (int i = 0; i < n; ++i) {
int num = abs(nums[i]);
if (num <= n) {
//对绝对值取负,保证有重复的数时仍然是负号标记
nums[num - 1] = -abs(nums[num - 1]);
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 1;
}
};
四、总结
时间复杂度:
废弃方法一:O(
n
2
n^2
n2),嵌套循环,由于nums.length范围很大,会超时。
废弃方法二:O(n),其中 n 是数组 nums 的长度。
方法一:O(n),其中 n 是数组 nums 的长度。
方法二:O(n),其中 n 是数组 nums 的长度。
空间复杂度:
废弃方法一:O(1),没有额外空间。
废弃方法二:O(n),创建了一个n大小的哈希表。
方法一:O(1),没有额外空间。
方法二:O(1),没有额外空间。
废弃方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
废弃方法一 | O( n 2 n^2 n2) | O(1) |
废弃方法二 | O( n n n) | O(n) |
方法一 | O( n n n) | O(1) |
方法二 | O( n n n) | O(1) |