JS算法之数组中数字出现的次数

109 阅读2分钟

这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

数组中数字出现的次数

剑指Offer 56 - I. 数组中数字出现的次数

难度:中等

题目:leetcode-cn.com/problems/sh…

一个整型数组nums里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(NN),空间复杂度是O(11)。

示例1:

输入:nums = [4,1,4,6]
输出:[1,6][6,1]

示例2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10][10,2]

限制: 2 <= nuns.length <= 10000

题解

题目要求时间复杂度是O(NN),空间复杂度是O(11),则不能通过暴力或者建表的方法去做。

位运算

在数组中判断两两元素是否相同,可以采用异或的方法。

对于数组中只出现一个不重复的元素,我们可以很轻松的通过全体异或的方式得到该值:a⊕a⊕b⊕b⊕c = 0⊕0⊕c = c,代码如下:

// @params nums[]
var singleNumber = (nums) => {
  let x = 0;
  for (let num of nums) {
    x ^= num;
  }
  return x;
}

console.log(singleNumber([2, 2, 4, 5, 4])) // 5

但是对于本题是不重复数字出现了两次,所以我们需要对其进行简化,步骤如下:

  1. 遍历nums执行异或,如a⊕a⊕b⊕b⊕c⊕d = 0⊕0⊕c⊕d = c⊕d

  2. 若c⊕d某二进制位为1,则c和d的二进制位一定不同,我们可以根据二进制位1,将数组拆分成两个子数组,并且每个数组中只有一个不重复数字。例如:

    对于[1,2,10,4,1,4,3,3],2和10是不重复数字,则

    2 -> 0010;10 -> 1010; 2和10这两个二进制数中首位不同,2与10得到8 -> 1000,即通过8来划分两个子数组。

  3. 拆分nums为两个子数组。

  4. 分别遍历两个子数组执行异或,这就将问题解耦啦,将两个子数组异或后的值返回即可。

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var singleNumbers = function (nums) {
  let x = 0, // 子数组1不重复的数字
    y = 0, // 子数组2不重复的数字
    n = 0, // 用于划分数组
    m = 1; // 用于划分数组
  // 1. 遍历异或
  for (let num of nums) {
    n ^= num;
  }
  // 2.循环左移,计算出m
  while ((n & m) === 0)
    m <<= 1;
  // 3.划分子数组 (num & m) === 1和(num & m) === 0,括号一定要加!
  for (let num of nums) {
    if ((num & m) !== 0) x ^= num; // 4.子数组异或
    else y ^= num;
  }
  return [x, y]; // 返回结果
};

//console.log(singleNumbers([1,2,10,4,1,4,3,3]))
  • 时间复杂度:O(NN)
  • 空间复杂度:O(11)

坚持每日一练!前端小萌新一枚,希望能点个哇~