DAY005_異或運算

運算規則

二進制:相同爲0 相異爲1

十進制:相同爲0 任何數字和0異或都是它本身

不利用額外變量交換兩個數

數組中一種數字出現了奇數次,其他數都出現了偶數次,怎麼得到這個出現了奇數次的數

將所有的數異或 得到的結果就是這個期望的數字

異或可以使用交換律,所有出現了偶數次的數字異或是0,出現了奇數次的數字異或得到這個數字本身

0和這個數字異或還是這個數字

public class Day006_1_找到出現了奇數次的數 {
    @Test
    public void test01(){
        int[] xx = {1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1};
        int eor = xx[0];
        for (int j = 1; j < xx.length; j++) {
            eor = eor ^ xx[j];
        }
        Assert.equals(eor, 9);
    }
}

數組中兩種數字出現了奇數次,其他數都出現了偶數次,怎麼得到這兩個出現了奇數次的數

先決條件注意異或的運算規則。見上文

假設兩種出現了奇數次的數字分別是 a 和 b,數組的所有元素異或得到的結果EOR = a ^ b ≠ 0
因此對於 EOR 任何一個是 1 的二進制位上,a 和 b 在這個二進制位上的數字 一個是1 另一個是0
因此如果 EOR 能只保留下一個是 1 的二進制位其餘位都歸零 得到一個數字 K,比如00001000
那麼K & aorK & b肯定一個是1 另一個是0& 運算只有11得1 其餘都得0

因此關鍵就是怎麼得到這個數字K

很簡單:一個數字的補碼 & 其相反數的補碼 就能夠得到
比如:一個正數 & 其相反數的補碼 = 正數補碼是它本身 & (正數取反得到相反數的反碼+1)
相關計算可以參考 001 中的舉例如下

通過下面的代碼可以得到 ab 中的其中一個

public class Day006_2_找到出現了奇數次的兩個數 {
    @Test
    public void test01(){
        //兩個數是8和9
        int[] xx = {1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1,8};
        int eor = 0;
        for (int i : xx) {
            eor = eor ^ i;
        }
        Assert.equals(eor, 8 ^ 9);
        //eor只保留一個是1的二進制位
        //例如:00000001 00001000 01000000
        int onlyOne = eor & (-eor);
        //注意異或的運算規則,相同爲0 相異爲1
        int one = 0;
        for (int j = 1; j < xx.length; j++) {
            //如果不等於0 說明是兩個數字其中的一個 或者是出現了偶數次的數字 循環完畢得到ab中的其中一個
            if ((xx[j] & onlyOne) != 0) {
                one = one ^ xx[j];
            }
        }
        int another = one ^ eor;
        Assert.isTrue((one == 8 && another == 9) || (one == 9 && another == 8));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章