题目描述
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
示例 2:
输入:nums = [3,4,-1,1]
输出:2
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
提示:
1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-missing-positive
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
此题在LeetCode上难度为困难,确实也挺难的,提交了几次才通过,通过的一次还时间空间双吊车尾。。。。。枯了。。。。
还是记录下,后面再优化。
思路在代码的注释上了,直接上代码吧。
代码
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
//总体放向,以nums内的元素做下标,染色下标指向的位置,再遍历nums,判断某位置是否被染色/标记来倒推元素是否存在
//需要处理的问题:
//问题1:题目求最小正数,最小正数为1,下标最低只能标记到1,显然,会漏掉0位置,因此以(元素 -1)做下标更为合理
//问题2:由于nums取值范不再被限定,正负数均有,需要想办法区分,nums内某位置的值是被染色/标记,还是它本来就是个负数。由于需要寻找的是个正数,可以预处理一遍nums,想办法将负数去掉:
// 1.直接去掉:将nums中的负数全部erase掉,但这样性能太差,循环erase时间复杂度也不满足O(n)
// 2.将负数置0:元素为0,得到下标0 - 1,即-1,染色/标记毫无意义,直接跳过,所以置0不影响结果,因此选择置0
int iSize = static_cast<int>(nums.size());
for(auto& elem : nums){ //预处理
if(elem < 0){
elem = 0;
continue;
}
if(elem > iSize){ //需要找的是最小正数,且下标为元素 - 1,所以大于nums长度的直接可以不用考虑了,也置0
elem = 0;
continue;
}
}
//以elem - 1 做index,去染色/标记index所指位置,染色/标记方式为将该位置的值改为负数
int iIndex = -1;
for(const auto& elem : nums){
if(0 == elem){ //index = 0 - 1,即-1,标记-1位置毫无意义:1.会越界;2.求得是正数,0不是正数
continue;
}else if(-(iSize + 1) == elem){ //特殊标记位置,跳过
continue;
}
iIndex = std::abs(elem) - 1; //elem所处位置可能被染色,abs处理来得到原值
if(nums[iIndex] < 0){ //已经染色的位置,不用再次染色,避免染色失效
continue;
}
if(0 == nums[iIndex]){ //>>>>>>>>>>iIndex位置的值为0,0无法使用乘以-1的方式染色,需要特别处理一下,赋值为-(iSize + 1)来染色<<<<<<<<<
nums[iIndex] = -(iSize + 1);
continue;
}
nums[iIndex] *= -1; //染色/标记index位置
}
int iMaxElem = 0; //记录下容器内的最大值
//从前往后,检查nums,查找未染色位置,首个未染色位置即为结果
for(iIndex = 0; iIndex < iSize; iIndex++){
if(nums[iIndex] >= 0){
return iIndex + 1; //index为元素 - 1,故所求元素应为index + 1
}
iMaxElem = iIndex + 1;
}
return iMaxElem + 1; //运行到这,说明容器内的元素全部能够被标记,那么未出现的值,一定比最大元素还大
}
};
优化
由于关心的是最小正数,于是在预处理阶段,将负数以及数字0,换为了比nums长度大的数,在染色的循环中,及跳过被预处理的数,又跳过本身就大于nums长度的数。减少了循环内的判断次数。提高了部分性能。
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int iSize = static_cast<int>(nums.size());
//预处理
for(auto& elem : nums){
if(elem <= 0){
elem = iSize + 1;
}
}
//以elem - 1下标然nums染色
int iIndex = 0;
for(const auto& elem : nums){
iIndex = std::abs(elem) - 1;
if(iIndex >= iSize){
continue;
}
if(nums[iIndex] < 0){
continue;
}
nums[iIndex] *= -1;
}
//寻找未染色位置
for(int i = 0; i < iSize; i++){
if(nums[i] > 0){
return i + 1;
}
}
return iSize + 1;
}
};
枯了,还是吊车尾。。。。