[ map || 数学 || 概率解法 ] 961. 在长度 2N 的数组中找出重复 N 次的元素

169 阅读2分钟

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

每日刷题 2022.06.04

题目

  • 给你一个整数数组 nums ,该数组具有以下属性:
    • nums.length == 2 * n.
    • nums 包含 n + 1 个 不同的 元素
    • nums 中恰有一个元素重复 n 次
  • 找出并返回重复了 n 次的那个元素。

示例

  • 示例1
输入: nums = [1,2,3,3]
输出: 3
  • 示例2
输入: nums = [2,1,2,5,3,2]
输出: 2
  • 示例3
输入: nums = [5,1,5,2,5,3,5,4]
输出: 5

提示

  • 2 <= n <= 5000
  • nums.length == 2 * n
  • 0 <= nums[i] <= 10^4
  • nums 由 n + 1 个 不同的 元素组成,且其中一个元素恰好重复 n 次

解题思路

  • nums中恰有一个元素重复n次,返回重复了n次的元素。第一时间想到的就是map集合统计计数,因为数组给的范围也很小。

拓展(random

Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值

简单的map集合

  • (无脑map)直接记录整个数组中每个数重复出现的次数,当遇到一个元素出现了n次,那么就返回这个数。
  • 后续分析:map做法还可以优化,也就是找到两个是相同的就可以返回。
    • 因为题目中说了:长度是2 * n,并且有n + 1个不同的数,且存在n个数是相同的,也就是说除了n个相同的数以外,剩下的所有的数都是不同的

AC代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var repeatedNTimes = function(nums) {
  let map = new Map();
  const n = nums.length, half = n / 2;
  // 记录数组中的每一个数出现的次数
  for(let i = 0; i < n; i++) {
    map.set(nums[i], (map.get(nums[i]) || 0) + 1);
    // 当遇到当前数在数组中的出现次数等于half,那么就返回
    if(map.get(nums[i]) == half) {
      return nums[i];
    }
  }
};

数学

  • 推导一下:那么当n > 2时的最大间隔是多少?最小间隔是多少?
    • n >= 2时,最大间隔是2个,最小间隔是0个
    • 因此对于当前的元素,只需要依次往后找3个进行判断即可

AC代码

  • 数学解法
/**
 * @param {number[]} nums
 * @return {number}
 */
var repeatedNTimes = function(nums) {
  const n = nums.length;
  for(let i = 0; i < n; i++) {
    // 分别判断,当前的数和间隔为0的数是否相等
              // 当前的数和间隔为1的数是否相等
              // 当前的数和间隔为2的数是否相等
    // 同时还要保证下标不会越界
    if((i + 1 < n && nums[i] == nums[i + 1]) || (i + 2 < n && nums[i] == nums[i + 2]) || (i + 3 < n && nums[i] == nums[i + 3])){
      return nums[i];
    }
  }
};

概率做法

  • 根据上述的分析,我们知道数组中只有1个数是可以重复的,且其重复了n次,那么现在只需要找到两个数相同就可以找到答案。
  • 在整个数组中,取到两个数相同的概率:(n / 2n) * (n - 1 / 2n) 约等于 1 / 4
    • 也就是说最好的情况下,我们随机选择4次就可以找到两个相同的数,但是也有可能比4次大。
    • 但是其始终是常熟级别的o(4) = o(1)

AC代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var repeatedNTimes = function(nums) {
  // 随机选择法
  const n = nums.length;
  while(true) {
    // 随机生成需要比较的两个下标 
    const x = Math.floor(Math.random() * (n));
    const y = Math.floor(Math.random() * (n));
    // 随机生成有可能生成两个相同的数,但是不能自己和自己比较,因此需要排除x == y的情况
    if(x != y && nums[x] == nums[y]) return nums[x];
  }
};