异或运算 求数组中出现奇数次的数

335 阅读2分钟

题目

一个数组中有1种数只出现奇数次,其余出现偶数次,求出现的奇数次的数,要求时间复杂度为O(n),空间复杂度为O(1)

  • 因为异或运算^满足交换律和结合律,且0^n=n;n^n=0;所以当只存在一个偶数次时,通过0和数组中所有值进行异或,偶数次出现的会被异或运算成0,最终计算的结果就是奇数次出现的数
let arr = [1, 1, 3, 3, 4, 4, 2, 2, 2];

let eor = 0;

for (let i = 0; i < arr.length; i++) {
  eor = eor ^ arr[i];
}
console.log(eor);

一个数组中有2种数只出奇数次,其余出现偶数次,求出现的奇数次的数,要求时间复杂度为O(n),空间复杂度为O(1)

  • 因为只有两种数出现奇数次,将0和所有数进行异或操作,最终只剩下出现奇数次的数的异或,假设为a^b
  • 因为是两种数,所以异或结果肯定不为0,必然两个数的某位是0和1相对的,即最终异或的结果有一位为1
  • 根据1所在的位对原数组进行分类,所有该位为1的为一组,为0的在另一组,因为a和b那位不同,所以a和b肯定在不同组
  • 因为只有a和b是奇数次,其余数都是偶数次出现,所以其余的数不管去到哪组,用0和分类后的组每个数进行异或,其余数都会被消掉,只留下a或b
  • 最终将上一步的结果和第一步的结果再进行异或,得到另一个数的结果

image.png

let arr = [1, 1, 3, 3, 4, 4, 2, 2, 2, 5, 5, 5];

let eor = 0;
let eorBit = 0;
//因为偶数次消掉,所以最终eor的结果为两个出现奇数次的数的异或,假设为eor=a^b
for (let i = 0; i < arr.length; i++) {
  eor = eor ^ arr[i];
}

//提取最右边第一个位为1的二进制,最终结果类似...000100...
let rightestOne = eor & (~eor + 1);

for (let i = 0; i < arr.length; i++) {
  //根据特定位不为0进行分类
  if (arr[i] & (rightestOne != 0)) {
    eorBit = eorBit ^ arr[i];
  }
}
//循环结束后eorBit就是两个出现奇数次的数的其中一个

//因为eor是两个出现奇数次的数的异或,假设为eor=a^b
//再次异或获取另一个数字
let otherOne = eor ^ eorBit;

console.log(eorBit, otherOne);