448. *Find All Numbers Disappeared in an Array
https://leetcode.com/problems/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 runtime? You may assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
解题思路
思路: 看到类似这种数组中的元素的值限定在 1 ~ N
之间的, 可能就可以考虑使用将元素值作为索引的方法… 由于每个元素值的大小都是 1 <= a[i] <= N
, 那么访问 a[a[i] - 1]
是没有问题的. 考虑上面这个数组:
# 假设数组的索引从 1 开始
index : 1 2 3 4 5 6 7 8
value : 4 3 2 7 8 2 3 1
现在从索引 1
开始, 如果依次将 a[abs(a[i])]
的值改为负数, 但如果 a[abs(a[i])]
已经是负数了, 那么就不用修改了.
# 假设数组的索引从 1 开始
index : 1 2 3 4 5 6 7 8
value : -4 -3 -2 -7 8 2 -3 -1
可以看到只有 {5, 6}
处的元素依然是正数, 那么消失的数字就是 {5, 6}
. 由于下面的代码中索引从 0
开始, 所以最后要加 1
.
核心思路就是: 对于 nums
中的每个元素, 将 nums[i] - 1
对应的那个元素设置为负值, 表示那个元素已经被访问过.
C++ 实现 1
通过将 nums[std::abs(nums[i]) - 1] > 0
的数值设置为负, 从而得到结果. 时间复杂度为 .
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
for (int i = 0; i < nums.size(); ++i)
if (nums[std::abs(nums[i]) - 1] > 0)
nums[std::abs(nums[i]) - 1] *= -1;
vector<int> res;
for (int i = 0; i < nums.size(); ++i)
if (nums[i] > 0)
res.push_back(i + 1);
return res;
}
};
扩展阅读
leetcode 上的解答:
Java accepted simple solution
The basic idea is that we iterate through the input array and mark elements as negative using nums[nums[i] -1] = -nums[nums[i]-1]
. In this way all the numbers that we have seen will be marked as negative. In the second iteration, if a value is not marked as negative, it implies we have never seen that index before, so just add it to the return list.
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> ret = new ArrayList<Integer>();
for(int i = 0; i < nums.length; i++) {
int val = Math.abs(nums[i]) - 1;
if(nums[val] > 0) {
nums[val] = -nums[val];
}
}
for(int i = 0; i < nums.length; i++) {
if(nums[i] > 0) {
ret.add(i+1);
}
}
return ret;
}
最为关键的是下面的解释:
大致意思可以理解为, 对于 nums
中的每个元素, 将 nums[i] - 1
对应的那个元素设置为负值, 表示那个元素已经被访问过, 之后判断 nums
中哪些值为正的, 如果为正, 说明对应的索引 i + 1
不存在在 nums
中.
https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/discuss/92956/Java-accepted-simple-solution/97460
A more detailed explanation for those who might still be confused:
This solution is using the relation between array index ([0, n-1])
and the given value range [1,n]
.
Each time when a new value X
is read, it changes the corresponding X
th number (value at index X-1
) into negative, indicating value X
is read for the first time. For example. using the given test case [4,3,2,7,8,2,3,1]
, when it comes to i = 2
in the first loop, this solution marks the 2nd
number (index = 1
), indicating we’ve found number 2
for the first time.
When we encounter a redundant number Y
, because we’ve marked the Yth
position (index Y -1
) when we saw Y
for the first time, the if clause won’t let us flip it again. This leaves the already marked Y
th number (number at index Y-1
) negative. For example, in the given test case, when i = 5
, val = |2| - 1 = 1
, nums[1] = -3 < 0
. No flip operation is needed because we’ve found value 2
before.
Looping through the 1st
loop takes time, flipping signs won’t take extra space.
The second loop checks the signs of the values at indices. If the sign at index P
is negative, it means value P + 1
is in the array. e.g. nums[0] = -4
, so value 0+1 = 1
is in the array. If the value at index Q
is positive, then value Q + 1
is not in the array. e.g. nums[4] = 8 > 0
, value 4 + 1 = 5
, we add 5
into the ret
list.