巧妙地利用位元算的特性可以以極低的時空複雜度解決問題
給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
思路: 位運算——陣列中兩兩異或即可!
因為有 a ^ a = 0
,0 ^ a = a
,並且異或運算滿足交換律 故對陣列[a,b,c,a,d,d,b] 透過異或運算: a ^ b ^ c ^ a ^ d ^ d ^ b
-> a ^ a ^ b ^ b ^ d ^ d ^ c
-> 0 ^ 0 ^ 0 ^ c
- > c
!
給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現了三次。找出那個只出現了一次的元素。
思路:求出每一位和,對3取餘即可!
因為只有一個數字出現一次,其他都是三次,說明在每一位上,如果特殊數字出現,那麼這一位上 1 的個數一定不是 3 的倍數!相反若其他數字在某位出現而特殊的沒有出現,則該位上的 1 的數量一定是 3 的倍數
public static int singleNumber (int[] A) {
// write code here
if(A == null || A.length == 0){
return -1;
}
int res = 0;
for(int i = 0;i < 32;i++){ //整數一共32位!所以每一位都得需要統計
int sum = 0;
for(int j = 0; j< A.length;j++){
sum += ((A[j] >> i) & 1); //統計第 i 位上 1 的數量
}
res += ((sum % 3)<<i); //注意左移i過去的需要右移i回來!!!!
}
return res;
}
延伸一下,對於某陣列若只有一個數字出現一次,其他數字都出現 n 次 (n > 1),則都可以透過統計每一位上 1 的數量來做!
給定一個整數陣列 nums,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。 找出只出現一次的那兩個元素。
輸入: [1,2,1,3,2,5]
輸出: [3,5]
思路:結合 題目(只出現一次的數字 I) 來做
因為本題有兩個數字(假設位a和b)只出現一次,其他都出現兩次,所以根據題目一可以馬上得到這兩個特殊數字的異或結果,即為 a ^ b。之後怎麼做?我們知道兩數的異或結果代表了兩個數字在某些位上的不同!
所以根據這個性質我們可以將該陣列分成兩部分,這兩部分分別只有一個特殊數字!之後分別全部異或一下就得到兩個特殊數字!
可以透過 a ^ b 從右開始的第一個出現的 1 所在的 位 作為區分標準!因為這個 1 代表了 a 和 b 在當前位的不同
public int[] singleNumber(int[] nums) {
if(nums == null || nums.length <= 1){
return new int[]{};
}
int xor = 0;
for(int n : nums){
xor ^= n; //求出兩數的異或
}
int bit = 1;
while((xor & 1) == 0){
//從左往右尋找第一個 1
xor >>=1;
bit <<= 1;
}
int val1 = 0;
int val2 = 0;
for(int n : nums){
if((n & bit) == 0){ //如果和當前bit位相同,則歸為一類
val1 ^= n;
}else{ //否則歸為另一類
val2 ^= n;
}
}
return new int[]{val1,val2};
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結