1.概念:異或運算(^)二進制形式的數據對應位上如果相等就為0,不相等就為1
-
0^n = n n^n = 0
-
異或運算滿足交換律和結合律
-
a^b = b^a
-
a^b^c = a^(b^c)
-
-
由異或運算的交換律和結合律可以得出:多個數據要進行異或操作最終形成一個數的值與這些數據進行異或運算的先后順序無關
-
實際上可以看成一種無進位相加,最終所得數據可以看進行運算的所有數據相同位上有幾個1,偶數個1那么最終結果該位上就為0,反之則為1
-
3^4^5
3=>0011 4=>0100 5=>0101
1)3^4 = 0011^0100 = 0111
0111^5 = 0111^0101 = 0010 = 2
2)3^5 = 0011^0101 = 0110
0110^4 = 0110^0100 = 0010 = 2
-
2.例題一:在一個整型數組中有一個數出現了奇數次,其余數都出現了偶數次,找出這個出現奇數次的數
-
解法:因為異或運算滿足交換律和結合律,所以可以先把數組中的數據進行排序,相同的數放在一起,而同一個數進行異或運算的結果為0,所以出現偶數次的所有數最終結果就是0,出現奇數次的數異或運算后最后一步一定是這個數和0進行異或運算,由此就可以找出出現奇數次的這個數了
-
public class OddNum {
public static void main(String[] args) {
int[] arr = {2,5,8,5,8,2,3,4,6,3,4,6,5};
int num = 0;
for (int i = 0; i < arr.length; i++) {
num ^= arr[i];
}
System.out.println(num);
}
}
3.例題二:一個整型數組中有兩個數出現了奇數次,其余數都出現了偶數次,找出這兩個出現奇數次的數
-
步驟1:設這兩個數為num1和num2,先將所有數據進行異或操作,得到的最終數據 num = num1^num2,保證num不為0
-
因為得到的最終數據不為0,所以這兩個出現奇數次的數表示為二進制的情況下,一定有某一位上對應一個1和一個0
-
-
步驟2:找出兩個出現奇數次的數異或運算結果 num 的最右邊的那一位1
-
做法:int rightOne = num & (~num + 1)
-
解釋1:異或運算結果最右邊那一位1一定就對應了進行運算的兩個數其中一個數的最右邊那一位1
-
解釋2:取反(~)后,~num最右邊那一位0就對應了原數據num最右邊的1,取反結果加1后,新出現的0變1的那一位1就是原數據num最右邊的1,此時取反加1后的結果與num進行&運算,只有0變1的這一位才會相同,所以最后就得到了num最右邊的那一位1了
-
-
步驟3:得到最右邊的1后,就可以將原來的數組中的數據按這一位分為兩組,一組是這一位為1的,一組是這一位為0的
-
找出這組數據中該位為1的那一組數
-
做法:遍歷數組,讓每個數據和rightOne進行&運算,只有該位為1的那些數的結果為0,從而將數據分成了兩組
int num1 = 0;
for (int cur : arr) {
if((cur & rightOne) == 0){
num1 ^= cur;
}
}
-
-
做法:用num和該位為1的那組數進行異或運算,相等於變成了從一堆數據中找出出現奇數次的那個數,設為num1
-
-
步驟4:此時得到了兩個數的異或結果num,和其中一個數num1
-
做法:num2 = num^num1
-
解釋:num2 = num^num1=num1^num2^num1=num2
-
-
-
public class TwoOddNum {
public static void main(String[] args) {
int[] arr = {1,4,5,2,6,3,6,5,4,3,2,1,4,2};
int num = 0;
for (int numx : arr) {
num ^= numx;
}
//num = num1 ^ num2
//num != 0
//num必然有一個位置上是1
int rightOne = num & (~num + 1);//提取出最右的1
int num1 = 0;
int num2 = 0;
for (int cur : arr) {
if((cur & rightOne) == 0){
num1 ^= cur;
}
}
num2 = num ^ num1;
System.out.println(num1 + " " + num2);
}
}