题目大意
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
解题思路
方法一:
遍历到位置i时,将位置i的元素nums[i]和位置nums[i]-1的元素进行交换,这样就能使得nums[i]的元素处于正确的位置。但是交换过来的元素不一定在正确位置,因此继续交换,直到nums[i]=i+1或者不符合条件。
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
if (nums.size() < 2)
return {};
vector<int> res;
for (int i = 0; i < nums.size(); ++i){
//如果该位置正确放置,则遍历下一个元素
if (nums[i] == i + 1)
continue;
// 如果该位置元素不是0,且后面的条件不满足
// 第二个条件是:当前元素nums[i]=4,应该被放到位置3,但是位置3上已经有个一个4,所以表示4重复了,可以退出while循环了
// 那么就可以把当前的nums[i]放到正确位置上
while (nums[i] != 0 && nums[nums[i] - 1] != nums[i]){
int tmp = nums[i];
nums[i] = nums[tmp - 1];
nums[tmp - 1] = tmp;
}
// 上述交换完成后,如果当前元素不是0,且没有正确放置,且应该正确放置的位置上已经有了一个相同的元素了,表示该元素是重复元素
if (nums[i] != 0 && nums[i] != i + 1 && nums[i] == nums[nums[i] - 1]){
res.push_back(nums[i]);
// 置为0.表示这个元素出现过一次了,后续不需要再进行判断
nums[i] = 0;
}
}
return res;
}
};
方法二:
遍历每一个元素,对于位置i的nums[i],按理说nums[i]这个元素应该放置在nums[nums[i]-1]这个位置,那么我们就将这个位置的元素变成负数,表示这个位置上的正确元素出现过一次了。
如果位置j的元素nums[j]的正确位置nums[nums[j]-1]已经是负数了,表示nums[j]这个元素出现过一次。
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
if (nums.size() < 2)
return {};
vector<int> res;
int index = 0;
for (int i = 0; i < nums.size(); ++i){
// 表示nums[i]应该被正确放置的位置是index
index = abs(nums[i]) - 1;
// 如果index位置的元素小于0,表示nums[i]已经出现过一次了
if (nums[index] < 0)
res.push_back(index);
else
// 反之表示nums[i]没出现过,因此记录一下
nums[index] = -nums[index];
}
return res;
}
};