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

291 阅读2分钟

LeetCode上的136编号的原题

知识储备:

异或 ^ 运算(同为0,异为1,可理解为无进位相加)

其满足交换结合律

A^B=B^A  (A^B)^C=A^(B^C)

0^N=N 0^0=0 1^1=1
/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    // 同为0,异为1
    let res = 0;
    forlet i=0;i<nums.length;i++){
     res = res^nums[i];
    }
    return res;
};

进阶版: 有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。

解析: 根据前面找一个不同数的思路算法,在这里把所有元素都异或,那么得到的结果就是那两个只出现一次的元素异或的结果。

然后,因为这两个只出现一次的元素一定是不相同的,所以这两个元素的二进制形式肯定至少有某一位是不同的,即一个为 0 ,另一个为 1 ,现在需要找到这一位。

根据异或的性质 任何一个数字异或它自己都等于 0,得到这个数字二进制形式中任意一个为 1 的位都是我们要找的那一位。

再然后,以这一位是 1 还是 0 为标准,将数组的 n 个元素分成两部分。

  • 将这一位为 0 的所有元素做异或,得出的数就是只出现一次的数中的一个
  • 将这一位为 1 的所有元素做异或,得出的数就是只出现一次的数中的另一个。
/**
 * @param {number[]} nums
 * @return {a,b}
 */
var singleNumber = function(nums) {
    // 同为0,异为1
    let eor = 0;
    forlet i=0;i<nums.length;i++){
     eor = res^nums[i];
    }
    //eor = a^b
    //eor != 0
    //eor 的数字二进制一定有一位是1,那样说明a,b在这一位上一个为0,一个为1
    
    let rightOne = eor & (~eor + 1) //取反加1 与本身与 提取出最右边的1
    let onlyOne = 0;
    forlet cur=0;i<nums.length;i++){
     if(nums[cur] & rightOne == 0){ 
       onlyOne ^= nums[cur];
     }
     eor = res^nums[i];
    }
    return {onlyOne,eor^onlyOne};
};