LeetCode #448 Find All Numbers Disappeared in an Array 数组
Description
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
思路
老年人回炉重造第6天。
这道题和 #442 是同类型的题,比较tricky,要求时间复杂度O(n)
,no extra space(题目补充说明了待返回的数组不算在 extra space 里,所以可以开辟一个数组保存算法的结果)
本题的难点之一是如何正确理解题目,关键信息有两个,一个是所有数字在输入串中只能出现一次
,另一个是1 ≤ a[i] ≤ n (n = size of array)
。该信息的意思是:数组内所有元素的值都在 1
与 size
之间。
本来必须有两层循环才能解题,或者将第二层改成 HashSet O(1)
~O(n)
的插入与查找。但是根据这两个关键信息,我们可以让内层循环变成常数时间的“映射”,从而使时间复杂度降为 O(n)
。内层“映射 Key-Value”的方法是:把元素减一的值作为 index
(Key),将 nums[index]
改为负号以便标记一个数字已经访问过了(Value)。
能这么做的基础有几个:
- 减一之后,数组内元素值的范围在
0
与size -1
之间,刚好与index
相等。 - 设计合理的映射 / Hash 策略,比如这里的基本原址、从
元素值的绝对值
到标记某个数字已被访问
的映射。 nums
值被改成负号也没有问题,依旧能用abs()
表示其原来的值。
AC之后发现:
- 对于这种重复数字问题,unoredered_set 查询效率很差,不能保持 O(1),因为相同元素间的碰撞太多了。因此,要根据该数组的特殊性重新定制新的映射 Hash 策略。
- c++ 两 set 之间做差集操作的效率低,且没有针对 unordered_set 的内置差集操作,而且意外的是,python 代码都比 c++ 代码快上不少。
第一版AC代码,340ms,ranking 5.5%:
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
vector<int> missing_num;
unordered_set<int> num_set;
for (int element : nums) {
num_set.insert(element);
}
for (int i = 1; i <= nums.size(); ++i) {
auto num_set_iter = num_set.find(i);
if (num_set_iter == num_set.end()) {
missing_num.push_back(i);
}
}
return missing_num;
}
};
第二版AC代码,python 712ms, ranking 5.5%;cpp 912ms,ranking 5% ::
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
a = set([i for i in range(1, len(nums)+1)])
b = set(nums)
return list(a - b)
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
set<int> all_nums;
for (int i = 1; i <= nums.size(); ++i) {
all_nums.insert(i);
}
set<int> nums_set(nums.begin(), nums.end());
set<int> missing_num;
set_difference(all_nums.begin(), all_nums.end(),
nums_set.begin(), nums_set.end(),
inserter(missing_num, missing_num.begin()));
return vector<int>(missing_num.begin(), missing_num.end());
}
};
第三版AC代码,116ms,ranking 64%:
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
if (nums.empty()) return nums;
vector<int> result;
for (auto element : nums) {
// element may be negative, don't use it directly as index
element = abs(element);
if (nums[element-1] > 0) {
nums[element-1] = -nums[element-1];
}
}
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] > 0) {
result.push_back(i+1);
}
}
return result;
}
};
————全心全意投入,拒绝画地为牢