【LeetCode】三題解決常見異或運算題
阿新 • • 發佈:2020-12-19
題目一
1.1 題目連結
136. 只出現一次的數字
1.2 題目描述
1.3 解題思路
1.位運算之異或操作
異或的性質如下
(1) 兩個數字異或的結果:a ^ b = 將 a 和 b 的二進位制每一位進行運算,得出的數字.
(2) 運算的邏輯是:如果同一位的數字相同則為 0,不同則為 1
(3) 任何數和本身異或則為0,6 ^ 6 = 0
(4) 任何數和0異或是本身, 6 ^ 0 = 6
(5) 異或滿足交換律。 即 a ^ b ^ c ,等價於 a ^ c ^ b
1.4 AC程式碼
class Solution { public int singleNumber(int[] nums) { int single = 0; for (int num : nums) { single ^= num; } return single; } }
題目二
2.1 題目連結
劍指 Offer 56 - I. 陣列中數字出現的次數
2.2 題目描述
2.3 解題思路
1.依舊是利用異或運算
異或的性質如下
(1) 兩個數字異或的結果:a ^ b = 將 a 和 b 的二進位制每一位進行運算,得出的數字.
(2) 運算的邏輯是:如果同一位的數字相同則為 0,不同則為 1
(3) 任何數和本身異或則為0,6 ^ 6 = 0
(4) 任何數和0異或是本身, 6 ^ 0 = 6
(5) 異或滿足交換律。 即 a ^ b ^ c ,等價於 a ^ c ^ b
與題目一相比,本題難度在於得把兩個不同的數字分開,分到兩個組中,然後在兩個組中分別利用題目一的解法即可求出答案。
核心思路:兩個不同的數字的二進位制表達中至少有一位不同,根據這一位不同結合&運算的結果即可把兩個數字分開,因為其餘數字都是兩兩相同,所以相同的數字必然是分到同一組中。我們只需要找出那位不同的數字mask,即可完成分組( & mask )操作。
num1: 101110
num2: 111110
num1^num2: 010000
可行的mask: 010000
num1&mask = 000000
num2&mask = 010000
這樣就成功把兩個不同的數字分為兩組,
2.4 AC程式碼
class Solution { public int[] singleNumbers(int[] nums) { int mask = 0; int[] ans = new int[2]; int ans1 = 0; int ans2 = 0; for(int i = 0; i < nums.length; i++){ mask = mask ^ nums[i]; } int bit = 0; while(true){ int a = mask & 1; if(a == 1){ mask = (int)Math.pow(2,bit); break; } mask = mask >> 1; bit++; } for(int i = 0; i < nums.length; i++){ int a = nums[i] & mask; if(a == 0){ ans1 = ans1 ^ nums[i]; }else{ ans2 = ans2 ^ nums[i]; } } ans[0] = ans1; ans[1] = ans2; return ans; } }
題目三
3.1 題目連結
劍指 Offer 56 - II. 陣列中數字出現的次數 II
3.2 題目描述
3.3 解題思路
這一題就不能單純的利用異或運算解決問題了。
考慮數字的二進位制形式,對於出現三次的數字,各 二進位制位 出現的次數都是 33 的倍數。因此,統計所有數字的各二進位制位中 11 的出現次數,並對 33 求餘,結果則為只出現一次的數字。
3.4 AC程式碼
class Solution {
public int singleNumber(int[] nums) {
int sum[] = new int[32];
for(int i : nums){
int tot = 0;
int temp = 1;
while(tot < 32){
int num = i & temp;
if(num != 0) sum[tot] += 1;
temp <<= 1;
tot++;
}
}
int ans = 0;
for(int i = 0; i < 32; i++){
if(sum[i] % 3 == 0){
sum[i] = 0;
}else{
sum[i] = 1;
ans += (int)Math.pow(2,i);
}
}
return ans;
}
}