题干
原题网址:
https://leetcode.com/problems/find-all-duplicates-in-an-array/description/
题干解析
给你一个数组,数组内的数据大小满足大于等于1且小于等于n(n为数组的长度)。且数据要么出现了一次,要么出现了两次,要你找出所有出现了两次的数值,并返回。
要求:不用新的空间,并在O(n)的时间复杂度内完成。
难度
中等
解题思路
本题乍一看以为不难,仔细一看要求挺苛刻的:不能用新的空间,而且时间复杂度要求O(n),所以不能用简单的计数来实现,也不能暴力搜索来实现,解这道题还是需要一些技巧的。
注意到这个数组的数据的大小要求比较严格,可以从这里进入作为突破点。因为数据的大小不会超过数组的长度,自然而然地想起了桶排的思想,但是不能用新的空间,只能在这个数组进行操作,所以,我考虑从数组头部(下标为0处,即i=0)开始往后遍历,设该位置为 i ,得到i位置的数值,找到该数值对应的坐标(如示例的第一个数值为4,那么找的下标应该是3,因为数组的下标是从0开始的,所以下标为3代表第4个),将找到的下标的位置做个标记(代码中我给它标为-1),但是这时候我们就要考虑到保护原来在下标为3的那个位置的数值(如果有未使用过的数值的话),既然当下 i 位置数值已经被用过了,那么当下 i 位置相当于是无用的,所以可以把下标为3的那个位置的数值放到当下 i 位置。这样往后遍历,如果出现这种情况,找到的下标的位置已经置为-1,说明该数值前面已经出现过了,所以这个数据就是我们要找的数据,将它放入要返回的矢量中。
代码
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
vector<int> ans;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != -1) {
int temp;
if (nums[nums[i] - 1] != -1) { // 如果此位置还有有效的数值
temp = nums[i] - 1;
nums[i] = 0;
if (nums[temp] != 0) {
nums[i] = nums[temp]; //把需要保护的数值放到现在的位置
i--; // 待会重新返回当下位置
}
nums[temp] = -1; //将找到的下标位置置为-1
} else {
ans.push_back(nums[i]); // 找到的重复的值放入数组中
nums[i] = 0; // 表示该位置为空
}
}
}
return ans;
}
};