YuArtian / blog

My~ B ! L ! O ! G ! 鸽鸽鸽鸽鸽鸽鸽鸽鸽...

Home Page:http://yuartian.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

只出现一次的数字

YuArtian opened this issue · comments

commented

只出现一次的数字

Round One !

请听题

​ 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

​ 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?( 时间复杂度 O(n) ,并且空间复杂度为 O(1) )

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

解法:

使用位操作,对所有元素做异或运算。

因为异或运算的性质是:任何一个数字异或它自己结果都是0 , 任何数字异或 0结果都是它本身。

通过交换律 和 结合律 我们可以将 (如示例2) 变为如下过程: (1⊕1)⊕(2⊕2)⊕4 ==> 0⊕0⊕4 ==> 4

具体代码实现

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var singleNumber = function (nums) {
    var res;
    nums.forEach(function (v) {
        res = res ^ v;
    });
    return res;
};

Roud Two !!

请听题

​ 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。(其余同上)

示例 :

输入: [1,2,1,3,2,4]
输出: [3,5]

解法

依然是异或,依然像第一题一样

先对所有元素依次做异或,最后的结果就是那两个只出现一次的异或结果

示例过程如下:(1⊕1)⊕(2⊕2)⊕(3⊕4) ==> 3⊕4, 得到结果 111 (二进制形式)。

那么接下来就是如何才能找到这两个只出现一次的元素。

因为任何一个数字异或它自己都等于 0,所以这个二进制数的每一个为 1 的位置其实都是我们要找的位置。

任意选取一个位置,将原数组分成两组,一组是在该位置全为 1 的数,另一组是在该位置全为 0 的数。

将这两组数分别做异或运算,得到的两个结果就是 两个只出现一次的数字。

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var singleNumber = function(nums) {
  let xor=0
  // 所有元素做异或运算
  for(let num of nums)xor ^= num
  // 为了方便起见,我们找最后一位为 1 的位置
  let lastOneBit=1
  while(xor % 2===0){
    xor /= 2
    lastOneBit*=2
  }
  let res=[0,0]
  for(let num of nums){
    // js 里的 & 的意思就是:对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
    // 所以可以通过 lastOneBit & num 来找到这个位置是 0 的所有元素
    if((lastOneBit & num)===0){
      res[0] ^= num
    }else{
      // 那个剩下的就是这个位置是 1 的所有元素
      res[1] ^= num
    }
  }
  return res
};

Round Three !!!

请听题

​ 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了次。找出那个只出现了一次的元素。

说明:

​ 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3

示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

解法

【笔记】网上大佬曾经说,如果能设计一个状态转换电路,使得一个数出现3次时能自动抵消为0,最后剩下的就是只出现1次的数。

开始设计:一个二进制位只能表示0或者1。也就是天生可以记录一个数出现了一次还是两次。

  • x ^ 0 = x;
  • x ^ x = 0;

要记录出现3次,需要两个二进制位。那么上面单独的x就不行了。我们需要两个变量,每个变量取一位:

  • ab ^ 00 = ab;
  • ab ^ ab = 00;

这里,ab都是32位的变量。我们使用a的第k位与b的第k位组合起来的两位二进制,表示当前位出现了几次。也就是,一个8位的二进制x就变成了16位来表示。

  • x = x[7] x[6] x[5] x[4] x[3] x[2] x[1] x[0]
  • x = (a[7]b[7]) (a[6]b[6]) ... (a[1]b[1]) (a[0]b[0])

于是,就有了这一幕 ...

它是一个逻辑电路,ab变量中,相同位置上,分别取出一位,负责完成00->01->10->00,也就是开头的那句话,当数字出现3次时置零。

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let a = 0, b = 0;
    for (let x of nums) {
        b = (b ^ x) & ~a;
        a = (a ^ x) & ~b;
    }
    return b;
};