Leetcode(448)——找到所有数组中消失的数字
题目
题解
注:下面两个方法其实都是使用了同样的原理,只是交互元素到对应下标索引上的方法不一样
方法1.1:
思路
1. 鸽笼原理(The Pigeonhole Principle)+
2. 交互元素到对应下表索引上
由题目知,nums 且 nums 中的数字一定属于 [1, n]。所以可以在第一个 for 循环中让 nums[i]
加上特定次数的 nums.size()
。而 nums[i]
加 nums.size()
的次数等于数组 nums 中 i+1 值的个数。
至于为什么是加 nums.size()
,是为了后续的判断。在最后一次 for 循环遍历 nums 时,如果 nums[i]
的值大于 nums.size()
,那么它就是在 [1, n] 范围内且出现在 nums 中的数字。所以第一次 for 循环所加值必须大于或等于 nums.size()
。
至于在第一次 for 循环时,在对某个值添加了 nums.size()
后,该如何知道其原值以继续执行让 nums[i]
加上特定次数的 nums.size()
。通过取余 (num - 1) % n
就可以获取其原值。因为 nums 的数字属于 [1, n]。
例子:[4,3,2,7,8,2,3,1] 在第一个 for 循环结束后是 [12,19,18,15,8,2,11,9]
nums[1] 被改变了2次得到19,即 nums 中出现了两次2,nums[2] 被改变了2次变成了18,即 nums 中出现了两次3。
代码实现
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
int n = nums.size(); // 增值必须大于或等于 nums.size()
for (auto& num : nums) {
int x = (num - 1) % n; // 获取原值
nums[x] += n;
}
vector<int> ret;
for (int i = 0; i < n; i++) {
if (nums[i] <= n) { // 原值一定属于 [1,nums.size()]
ret.push_back(i + 1);
}
}
return ret;
}
};
复杂度分析
时间复杂度:
O
(
N
)
O(N)
O(N) ,
N
N
N 表示数组的长度
空间复杂度:
O
(
1
)
O(1)
O(1)
方法1.2:
思路
1. 将所有正数作为数组下标,置对应数组值为负值。
2. 那么,仍为正数的位置即为消失的数字。
思考了一下为什么必须是
nums[abs(nums[i])-1] = -abs(nums[abs(nums[i])-1]);
因为如果直接设置成0,那后面遍历到这个位置的时候就没办法使用它的值了; 而如果直接乘以-1设置成相反数,那么后面遍历到重复元素的时候,这个负数又会变回正数,从而对结果产生影响; 因此只能等于负的绝对值,确保万无一失。
举个例子:
原始数组:[4,3,2,7,8,2,3,1]
重置后为:[-4,-3,-2,-7,8,2,-3,-1]
结论:[8,2] 分别对应的 index 为 [5,6] (消失的数字)
代码实现
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
// abs(n) 函数返回 n 的绝对值
for (int i = 0; i < nums.size(); ++i)
nums[abs(nums[i])-1] = -abs(nums[abs(nums[i])-1]);
vector<int> res;
for (int i = 0; i < nums.size(); ++i){
if (nums[i] > 0)
res.push_back(i+1);
}
return res;
}
}
复杂度分析
时间复杂度:
O
(
N
)
O(N)
O(N) ,
N
N
N 表示数组的长度
空间复杂度:
O
(
1
)
O(1)
O(1)