題目一
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;
}
}