持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
每日刷题 2022.06.12
- leetcode原题链接:leetcode.cn/problems/fi…
- 难度:中等
- 方法:抽屉原理
题目
- 给你一个长度为 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.length1 <= n <= 1051 <= nums[i] <= nnums中的每个元素出现 一次 或 两次
解题思路
- 根据题意分析:数组
nums中的所有整数都在[1, n]以内,且每个都出现一次或者两次。需要返回所有出现两次的整数。
做法1
- 因为
nums中的所有整数都是在[1, n]之间的,并且有些整数会出现两次,那么也就意味着有些整数并不会出现在数组中。- 通过区间
[1, n]映射,想到数组nums的下标范围:[0, n - 1] - 那么对于
nums中的每一个元素,都应该有一个确定的位置,即:nums[i] = nums[nums[i] - 1] - 如果当前的元素
nums[i]并不在正确的位置nums[nums[i] - 1]上,那么就需要将与正确位置上的数进行交换,使当前的数处于正确的位置上。
- 通过区间
- 最终,结果数组记为
ans, 将排好的数组遍历一遍,记录下没有在正确位置的数值。
做法2
- 在做法一的基础上优化,这种做法更巧妙。无需交换,只需要将
当前位置的元素nums[i], 所对应的正确的位置的值nums[nums[i] - 1]*-1* -1表示:当前的元素应该被找到过一次,如果下次再找到的话,就表示是第二次找到,就可以直接加入到结果数组中。
- 那么只需要一遍
for循环遍历,对于每个位置取绝对值|nums[i]|计算, 其所对应的正确位置。 - 最终直接返回
ans数组即可。
AC代码
- 做法1
/**
* @param {number[]} nums
* @return {number[]}
*/
var findDuplicates = function(nums) {
const n = nums.length;
for(let i = 0; i < n; i++) {
// 当前位置的数不在其对应所在的位置时,需要将其调整到正确的位置
while(nums[i] != nums[nums[i] - 1]) {
const idx = nums[i] - 1;
let a = nums[i], b = nums[idx];
// 交换两个数的方法,不使用第三变量
a = a + b;
b = a - b;
a = a - b;
nums[i] = a,nums[idx] = b;
}
}
let ans = [];
// 因为题目中说了所有的数字都在`[1, n]`之间,且有些数字会重复2次,那么就必然会有一些数字是不会出现的,因此只需要遍历查看哪个元素没有在正确的位置上即可。
for(let i = 0; i < n; i++) {
if(nums[i] - 1 != i){
ans.push(nums[i]);
}
}
return ans;
};
- 做法2
/**
* @param {number[]} nums
* @return {number[]}
*/
var findDuplicates = function(nums) {
const n = nums.length;
let ans = [];
for(let i = 0; i < n; i++) {
let idx = Math.abs(nums[i]) - 1;
if(nums[idx] < 0){
// 已经被找到过一次
ans.push(Math.abs(nums[i]));
}else {
// 找到合适的位置
nums[idx] *= -1;
}
}
return ans;
};