位運算
位異或
- 異或的性質:兩個數字異或的結果a^b是將 a 和 b 的二進制每一位進行運算,得出的數字。 運算的邏輯是如果同一位的數字相同則爲 0,不同則爲 1。
- 異或的規律:(1)任何數和本身異或則爲0;(2)任何數和 0 異或是本身;(3)異或滿足交換律。 即 a ^ b ^ c ,等價於 a ^ c ^ b。
只出現一次的數字(其餘都出現兩次) -> (1)可以用unordered_map記錄數組元素出現的次數;(2)更快的方式是直接對所有元素進行異或操作,由於其他元素都出現兩次(任何數和本身異或則爲0),而且異或滿足交換律,所以其他元素異或的結果應該爲0,最後結果爲這個只出現一次的數。
int singleNumber(vector<int>& nums) {
int k = 0; // 性質2,任何數和0異或是本身,所以初始值取0
for(int i = 0; i < nums.size(); i++){
k^=nums[i];
}
return k;
}
vector<int> singleNumbers(vector<int>& nums) {
int k = 0;
for(int num:nums){
k^=num;
}
// 假設這兩個數字爲a和b,此時k=a^b
// 將nums裏的數字分兩組,目標是a和b在不同的組裏
int pos = 0, a = 0, b = 0;
for(int i = 0; i < 32;i++){
if(k & 1 == 1){ // 找到k中爲1的位,說明a和b在該位是不同的
pos = i;
break;
}
k = k >> 1;
}
// 根據找到的pos進行分組,a和b在不同組
for(int num:nums){
if((num >> pos) & 1 == 1){
a^=num;
}else{
b^=num;
}
}
vector<int> v{a,b};
return v;
}
找到數組中只出現一次的數(其餘都出現三次) -> (1) 哈希表存儲和查找,使用unordered_map;(2) 如果某個數字出現3次,那麼這個3個數字的和肯定能被3整除,則其對應二進制位的每一位的和也能被3整除;統計數組中每個數字的二進制中每一位的和,判斷該和是否能被3整除。若可以,則只出現一次的數字的二進制數中那一位爲0,否則爲1。參考
int singleNumber(vector<int>& nums) {
int k = 0;
for(int i = 0; i < 32; i++){
int count = 0;
for(int num:nums){
if((num>>i)&1 == 1){
count++;
}
}
// 出現三次的所有數字的每一位上的和都能被3整除,如果count%3==1表明這個只出現一次的數在這一位爲1,否則爲0
if(count%3==1){
k += 1<<i;
}
}
return k;
}
相關練習
二進制中1的個數
不用加減乘除做加法 -> 位異或操作可以模擬二進制無進位的加法,位與操作可以模擬二進制的進位(位與的結果需要左移1位)。a可以當做無進位的和,b當做進位,然後不斷重複之前的操作,直到進位b爲0,a便是和。
int add(int a, int b) {
int sum, carry;
while(b!=0){
sum = a^b;
carry = (unsigned int)(a&b) << 1; // C++負數無法左移,需要轉換成無符號整型
a = sum;
b = carry;
}
return a;
}