LeetCode 面试题56 - I. 数组中数字出现的次数 题解

313 阅读2分钟

面试题56 - I. 数组中数字出现的次数

题目

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

思路

  • 题目中需要找出只出现了一次的的两个数字,并且其他数字只出现了两次。
  • 因为其他数字只出现了两次,又因为我们知道两个相等的数字进行异或运算可得 0 ,及 a & a = 0 ,不相同的一定不会为 0 。
  • 如果仅有一个数字只出现一次的话,那么就可以通过全部数字进行异或运算即可得到出现了一次的数字。
  • 但是此题中出现了一次的数字有两个,那么该怎么做呢?
  • 我们可以想到将这堆数字分成分别含有两个出现了一次数字的两堆,且每一堆中除了一个出现了一次的数字,其他都是成对的。
  • 我们可以想到两个不同的数字做异或的结果的二进制中有数字 1 的位置,一定是这两个数字的二进制中其中一个数字在这个位置的数字为 1 ,而另一个为 0 ,例如:1010 0101 & 0001 1010 = 1011 0100
  • 那么我们根据任意这样的位置来将这一堆数字进行分成两类。而且二进制不同的话数字一定也不一样,也可以很好的符合之前的需求。

代码

  1. 先要得到那两个数字的异或值,及全部数字进行异或。
let sum = nums.reduce((a, b) => a ^= b);
  1. 得到可以通过二进制来表示异或值sum在二进制中任意一个有 1 的位置。
let key = 1;
while (!(sum & key)) {
  key <<= 1;
}
  1. 对一堆数字分组,并且对任意一堆进行异或运算。
let sum_1 = 0;
nums.map(num => {
  (num & key) && (sum_1 ^= num);
});
  1. 将得到的结果sum_1sum进行异或算出另一个数字,并返回。
return [sum_1, sum ^ sum_1];
  1. 整合
/**
* @param {number[]} nums
* @return {number[]}
*/
var singleNumbers = function (nums) {
  let sum = nums.reduce((a, b) => a ^= b);
  let key = 1;
  let sum_1 = 0;

  while (!(sum & key)) {
    key <<= 1;
  }
  nums.map(num => {
    (num & key) && (sum_1 ^= num);
  });
  return [sum_1, sum ^ sum_1];
};

console.log(singleNumbers([1,2,10,4,1,4,3,3]));

用时及内存

用时及内存