位运算算法详解2

130 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

异或运算

异或运算其实是无进位相加,满足交换律和结合律

也就是

a ^ b = b ^ a

(a ^ b) ^ c = (a ^ c) ^ b

a ^ a = 0

交换两个数的异或写法如下:

let a = 3 
let b = 8 
a = a ^ b 
b = a ^ b // a ^ b ^ b => a
a = a ^ b // a ^ b ^ a => b
// 即可交换两数

这里需要注意:异或运算做交换需要满足条件,两个数的地址不一致,如果在数组中,交换的是同一个下标位置,则会将该位置变成0

关于异或运算可以看一个例子如下:

一个数组中,1个数出现奇数次,其他数出现偶数次,求这个数

根据异或运算满足交换律和结合律,那么只需要不断异或这个数组中的每个元素,最终得到的数一定是这个奇数次的数

function getNum (arr) {
  let num = 0
  for (let i = 0; i < arr.length; i++) {
    num ^= arr[i]
  }
  return num
}

一个数组中,2个数出现奇数次,其他数出现偶数次,求这2个数

根据上题,变形出的第二题

  • 如果按照上述思路,全部异或,最后得到的是这两个数的异或结果a^b
  • 可以通过把结果x & -x得到最右边的1,这个1,一定是这两个奇数其中一个的某一位,假设为a而数组中其他元素的这个位置加起来一定为偶数个
  • 那反向思考,这个位置不为1的数,一定包含b和其他偶数,而不包含a,这样就能求出b
  • b和a^b相异或,然后就能得到a

代码如下:

function getNum2 (arr) {
  let eor = 0
  for (let i = 0; i < arr.length; i++) {
    eor ^= arr[i]
  }
  let right = eor & -eor
  let num = 0
  for (let s of arr) {
    if ((right & s) === 0) {
       num ^= s
    }
  }
  return [num, num ^ eor]
}

下面看一道例题

389. 找不同

  • 依据题意,可以很容易的想到异或算法
  • 但是有个问题,字符如何异或呢?需要先将字符转成ascii值,再进行异或运算,运算完成后再转回字符 代码如下:
var findTheDifference = function(s, t) {
    // 对数字,相同异或为0,不同异或为1
    // 对ascii异或,最后再转为字母
    let ret = 0
    for (let ch of s) {
        ret ^= ch.charCodeAt()
    }
    for (let ch of t) {
        ret ^= ch.charCodeAt()
    }
    // 转为字母
    return String.fromCharCode(ret)
};