[抽屉原理] 442. 数组中重复的数据

184 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

每日刷题 2022.06.12

题目

  • 给你一个长度为 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 <= 105
  • 1 <= nums[i] <= n
  • nums 中的每个元素出现 一次 或 两次

解题思路

  • 根据题意分析:数组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;
};