题目描述
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
示例 1:
输入: [1,2,0]
输出: 3
解题思路
这种题还是有一些trick的(太难,面试不友好),理解并尽量记住。本题可用“哈希表”实现时间
O
(
n
)
O(n)
O(n),但是这时空间复杂度不满足要求。“脏工作环境” 的解决方法是将一个字符串 hash_str 分配 n 个 0,并且用类似于哈希表的方法,如果在数组中出现数字 i 则将字符串中 hash_str[i] 修改为 1 。(空间其实还是
O
(
n
)
O(n)
O(n))
-
bitmap法:(思路:使用索引作为“哈希键”以及元素的符号作为“哈希值”来实现是否存在的检测。拿自己当bitmap,不再另外设置哈希表,绝妙!)
首先我们可以不考虑负数和零,因为不需要考虑。同样可以不考虑大于
n的数字,因为首次缺失的正数一定小于或等于n + 1。缺失的正数为n + 1的情况会单独考虑。(n个数只可能缺失1~(n+1))-
检查
1是否存在于数组中。如果没有,则已经完成,1即为答案。 -
如果
nums = [1],答案即为2。 -
将负数,零,和大于
n的数替换为1。(目的是将nums数组全变为正的,便于后面用负号来表示一个数曾经出现过) -
遍历数组。当读到数字
nums[i]时,替换第nums[i]个元素的符号。(下标为nums[i] - 1)注意重复元素:只能改变一次符号。(见代码,我的实现)
-
再次遍历数组。返回第一个正数元素的下标(+1)。
-
如果之前的步骤中没有发现
nums中有正数元素,则返回n + 1。
例如,
nums[2]元素的负号意味着数字3出现在nums中。nums[3]元素的正号表示4没有出现在nums中。为了完成此操作,我们遍历一遍数组(该操作在数据预处理使得数组中只有正数的操作后),检查每个元素值
elem(下标) 以及将nums[nums[elem] - 1]元素的符号变为负号来表示数字nums[elem]出现在nums中。注意,当数字出现多次时需要保证符号只会变化 1 次。 -
-
桶排序(推荐):看题解幻灯片,解法非常优雅,强烈推荐。(上面那个解法了解即可,主要是会用“负号”表示一个数曾经出现过的思路)
-
核心思想:一个萝卜一个坑,最后再看一下哪个坑出了问题。
-
拿上面的示例举例,数字 1 应该放在索引为 0 的位置上,数字 3 应该放在索引为 2 的位置上,数字 4 应该放在索引为 3 的位置上。一个数字放在它应该放的位置上,我们就认为这个位置是“和谐”的,看起来“顺眼”的。
-
按照以上规则排好序以后,缺失的第 1 个正数一下子就看出来了,那么“最不和谐”的数字的索引 +1,就为所求。那如果所有的数字都“和谐”,数组的长度 +1 就为所求。
-
参考代码
bitmap法
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int length = nums.size();
if (length == 0)
return 1;
// 基本情况,先看含不含1
bool contain_1 = false;
for (auto i : nums) {
if (i == 1) {
contain_1 = true;
break;
}
}
// 两种特殊情况:数组不含1 和 nums = [1]
if (!contain_1)
return 1;
//if (contain_1 && length == 1) // 这个特判其实不需要
// return 2;
// 用 1 替换负数,0,和大于 n 的数。在转换以后,nums 只会包含正数
for (int i = 0; i < length; i++){
if (nums[i] <= 0 || nums[i] > length)
nums[i] = 1;
}
// 使用索引和数字符号作为检查器
// 例如,如果 nums[i] 是负数表示在数组中出现了数字 `i + 1`
// 如果 nums[2] 是正数 表示数字 3 没有出现
for (int i = 0; i < length; i++) {
int curNum = abs(nums[i]);
// 如果发现了一个数字 curNum,则改变 nums[curNum - 1] 元素的符号
// 注意重复元素只需操作一次(这里不要出错)
nums[curNum - 1] = -abs(nums[curNum - 1]);
}
// 现在第一个正数的下标,就是第一个缺失的数
for (int i = 0; i < length; i++){
if (nums[i] > 0)
return i + 1;
}
return length + 1;
}
};
桶排序(推荐!!!爱死我自己哈哈哈哈哈)
// 基于桶排序。
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int length = nums.size();
if (length == 0)
return 1;
// 小心[1, 1]这种例子会造成死循环,要特殊处理。(nums[i] != nums[nums[i]-1])
for(int i = 0; i < length; i++){
while(nums[i] >= 1 && nums[i] <= length && i != nums[i]-1 && nums[i] != nums[nums[i]-1]) // 注意这里要用while
swap(nums[i], nums[nums[i]-1]);
}
for(int i = 0; i < length; i++){
if(i + 1 != nums[i])
return i+1;
}
return length + 1;
}
};
本文介绍了一种高效算法,用于在未排序的整数数组中找到最小的缺失正整数,采用比特位标记和桶排序策略,实现O(n)时间复杂度与常数级空间复杂度。
1071

被折叠的 条评论
为什么被折叠?



