一、问题描述
给你一个长度为 n 的整数数组 nums ,其中 nums 的所有整数都在范围 [1, n] 内,且每个整数出现 一次 或 两次 。请你找出所有出现 两次 的整数,并以数组形式返回。
你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问题。
二、示例及约束
示例 1:
输入: nums = [4,3,2,7,8,2,3,1]
输出: [2,3]
示例 2:
输入: nums = [1,1,2]
输出: [1]
示例 3:
输入: nums = [1]
输出: [ ]
提示:
● n == nums.length
● 1 <= n <=
1
0
5
10^5
105
● 1 <= nums[i] <= n
● nums 中的每个元素出现 一次 或 两次
三、代码
方法一:交换元素到对应位置
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
/*由于有数字出现了2次,则表示有数字没有出现过,因此可以将每个数放在对应的位置,
其余的数即为题设所需,数组下标范围是[0,n-1],因此数i要放到i-1的位置上
*/
for (int i = 0; i < n; ++i) {
//判断该数是否已经放置到正确的位置,如果没有的话,将其放置到nums[i]-1的位置上
while (nums[i] != nums[nums[i] - 1]) {
swap(nums[i], nums[nums[i] - 1]);
}
}
vector<int> ans;
//下标不一致的数会在连续的位置上,依次进行输出即可
for (int i = 0; i < n; ++i) {
if (nums[i] - 1 != i) {
ans.push_back(nums[i]);
}
}
return ans;
}
};
方法二:使用正负号作为标记
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
vector<int> ans;
/*由于次数只有1次和2次,可以用负号表示出现了1次,正号表示出现了2次,
在遍历过程中,若遍历到数nums[i]时,将i对应的下标nums[x-1]其变为负,表示遍历过1次,若再次遍历到nums[i]时,则此时的nums[x-1]已经是负数,此时将其绝对值x输出,即可满足题设需求
*/
for (int i = 0; i < n; ++i) {
int x = abs(nums[i]);//由于数组下标和输出结果为正,加绝对值
//如果值为正,说明第一次遍历到这个数,取负
if (nums[x - 1] > 0) {
nums[x - 1] = -nums[x - 1];
}
//如果值为负,说明第二次遍历到这个数,输出绝对值x
else {
ans.push_back(x);
}
}
return ans;
}
};
四、总结
由于题设要求O(1)的空间复杂度,所以两种方法都是对原数组进行修改。
时间复杂度:
方法一:O(n),每一次交换操作会使得至少一个元素被交换到对应的正确位置,因此交换的次数为 O(n),总时间复杂度为 O(n)。
方法二:O(n),只需要对数组 nums 进行一次遍历。
空间复杂度:
均为O(1)
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
方法一 | O( n n n) | O(1) |
方法二 | O( n n n) | O(1) |