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

题目描述

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

 

思路:位运算

  • 异或运算复习

功能:相同为0,不同为1

运用:与0异或不变,与1异或取反

       根据异或的性质很容易得出,假设数组中只有一个数字只出现了一次,那么数组中所有数字异或的结果,就是这个“落单”的数字。

       因此对于这道题目,如果能设法把数组分成两部分,使得:

    1. 落单的两个数分别在不同的组中
    2. 相同的两个数分到同一组

      那么对两个组分别异或,就可以得到答案。

      接下来的问题是找到这样的分组标准。假设数组所有元素异或的结果为X,落单的两个数为A和B,则X就是A与B异或的结果。假设 Xi 代表X二进制表示的第i位,则当 Xi 为1时,根据数字二进制表示的第i位为0或为1,可以得到满足上述两个条件的两个分组,原因如下:

  1. 对于成对出现的数 - 第i位必然也相同,因此会被同时分到“1组”或“0组”
  2. 对于落单的A和B - 由于X是A与B异或的结果,则 Xi 也是 Ai 和 Bi 异或的结果。 Xi = 1,所以 Ai != Bi , 因此根据第i位的值来分组必然使得A与B被分到两个组。

 

步骤:

  1. 遍历数组,全部异或,得到X
  2. 初始化bitOne = 1,若X&bitOne为0,将bitOne右移一位,重复此步直到X&bitOne为1
  3. 初始化 a,b = 0,遍历一遍数组,如果第bitOne位为1,和a异或;为0,和b异或。
  4. a,b为最终结果。

 

代码:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var singleNumbers = function(nums) {
    let  X = 0;
    for(let num of nums){
        X ^= num;
    }
    
    let bitOne = 1;
    while(!(bitOne&X)){
        bitOne <<= 1;
    }
    
    let a = 0, b = 0;
    for(let num of nums){
        if(bitOne & num){
            a ^= num;
        }else{
            b ^= num;
        }
    }
    
    return [a,b];
};

时间复杂度:O(N)

空间复杂度:O(1)

 

PS: 得到只有第N位为1的二进制数 —— 将 1 左移N位

posted @ 2020-07-08 05:36  studystudyxinxin  阅读(155)  评论(0)    收藏  举报