按位操做符-按位異或(XOR)

上一章咱們認識了按位操做符, 這一章咱們整理一下按位運算的經典案例html

帶你認識按位操做符數組

回顧

使用按位操做符的數,會先轉成 32 位比特序列,也就是32 位的有符號的整數markdown

若是這個數是正數,若是大於 2 31 1 2^{31}-1 ,只會保留低 32 位, 高於 32 位的數不存儲;app

若是這個數是負數,若是小於 2 31 -2^{31} ,只會保留低 32 位, 高於 32 位的數不存儲;post

按位異或概述

兩個二進制數, 它們對應位的數只有 1 個 1, 結果爲 1, 不然, 結果爲 0。spa

性質

  1. 自反性: a ^ b ^ b = a
  2. 交換律: a ^ b = b ^ a
  3. 結合律:a ^ (b ^ c) = (a ^ b) ^ c
  4. 恆等律: a ^ 0 = a
  5. 歸零律: a ^ a = 0

浮點數轉整數

12.12 ^ 0   //12
Math.PI ^ 0  //3
複製代碼

JavaScript 默認將數字存儲爲 64 位浮點數,但按位運算都是以 32位的二進制整數執行。code

兩個相同的數按位運算後, 結果只會保留整數部分,小數部分不存儲。orm

Infinity ^ 0 //0
Number.MAX_VALUE ^ 0 //0
複製代碼

若是一個數大於 2 31 1 2^{31}-1 , 按位運算時只會保留低 32 位運算, 高於 32位的數丟棄, 結果就不許確了。xml

兩個相同的數異或爲 0

3 ^ 3           //0
12.12 ^ 12.12   //0
複製代碼

不用臨時變量,交換兩個整數的值

let a = 3, b =5;
a = a ^ b;
b = a ^ b;  // 分解開就是 (a ^ b) ^ b, 根據自反性得知,結果爲 a,a 賦值給 b
a = a ^ b;  // 分解開就是 a ^ (a ^ b), 根據自反性得知,結果爲 b,b 賦值給 a

console.log(a, b)  // 5, 3
複製代碼

咱們畫圖看一下:htm

a = 3 (base 10) = 00000000000000000000000000000011 (base 2)
 b = 5 (base 10) = 00000000000000000000000000000101 (base 2)
                   --------------------------------
 3 ^ 5 (base 10) = 00000000000000000000000000000110 (base 2) = 6 (base 10) = a
 
 //如今 a = 6了
 a = 6 (base 10) = 00000000000000000000000000000110 (base 2)
 b = 5 (base 10) = 00000000000000000000000000000101 (base 2)
                   --------------------------------
 6 ^ 5 (base 10) = 00000000000000000000000000000011 (base 2) = 3 (base 10) = b
 
 //如今 b = 3 了, 已經把 a 的值交換給 b 了
 a = 6 (base 10) = 00000000000000000000000000000110 (base 2)
 b = 3 (base 10) = 00000000000000000000000000000011 (base 2)
                   --------------------------------
 6 ^ 3 (base 10) = 00000000000000000000000000000101 (base 2) = 5 (base 10) = a
複製代碼

通過三次 ^操做後, 咱們把 a 和 b的值交換成功了。

找出重複的數

1-1000放在含有1001個元素的數組中,只有惟一的一個元素重複,找出這個重複的數字。

設重複的數爲 x, 根據自反性能夠獲得:
(1 ^ 2 ...... 999 ^ 1000)^ (1 ^ 2 ...... 999 ^ 1000 ^ x) = x
複製代碼

找惟一不一樣的數

找出惟一一個在數組中出現一次的整數,而其餘都會出現兩次。

咱們知道兩個相同的數異或爲 0, 那麼把數組中的全部整數異或運算, 剩下的就是惟一出現一次的數。

let arr = [1,2,1,2,3]
let value = arr.reduce((p, c) => p ^ c)
console.log(value)    //3
複製代碼

找出不一樣的兩個數

一個整型數組 nums 裏除兩個數字以外,其餘數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。

咱們把這一題拆分紅上面的那道題就好作了,拆分紅兩個數組分別異或。

  1. 把兩個不一樣的數拆分到兩個數組中。
  2. 把兩個相同的數放到同一個數組中。
const nums = [1, 4, 3, 4]
const x = nums.reduce((p, c) => p ^ c)    //x = a ^ b 
let mark = 1;
//兩個不相同的數, 異或對應位的數字至少有 1 位不相同, 咱們找出這個數.
while((x & mark) === 0) mark <<= 1; 
let xor1 = 0;
let xor2 = 0;

for (let n of nums) {
    if(n & mark){   
        xor1 ^= n;
    }else{
        xor2 ^= n;
    }
}

console.log(xor1, xor2) //3,1
複製代碼

設 兩個不一樣的數是 a 和 b

  1. 根據異或自反性可得出,數組全部數異或的結果是 x = a ^ b
  2. a 和 b是不相同的數, a ^ b的結果x確定不爲 0,那麼至少有 1 位是 1, 咱們找出是哪一位?就能夠分組了。
  3. 咱們定義一個 mark=1 ,mark 會左移, 根據 x & mark去找最近一位的 1,若是 x & mark等於 1, 找到位置,不然, mark 繼續左移。
  4. 位移操做mark,咱們得知mark中只有 1 位是 1, 其餘位都是 0, 因此遍歷nums判斷n & mark可得出:
  5. 把結果是 1 的放到 xor1 組,不然,放到xor1組。
  6. 相同的數 n & mark的值是同樣的, 確定會放在同一個 xor中。

總結

  1. 兩個相同的數,異或結果爲 0
  2. 使用異或的自反性,能夠交換兩個數。
  3. 按位與和位移結合使用,能夠判斷標記位
  4. 用分治法把複雜問題簡單化。
相關文章
相關標籤/搜索