目錄
- 技巧
- 練習位運算
- [461. 漢明距離](https://leetcode-cn.com/problems/hamming-distance/)
- [190. 顛倒二進制位](https://leetcode-cn.com/problems/reverse-bits/)
- [136. 只出現一次的數字](https://leetcode-cn.com/problems/single-number/)
- [260. 只出現一次的數字 III](https://leetcode-cn.com/problems/single-number-iii/)
- [268. 丟失的數字](https://leetcode-cn.com/problems/missing-number/)
- [693. 交替位二進制數](https://leetcode-cn.com/problems/binary-number-with-alternating-bits/)
- [476. 數字的補數](https://leetcode-cn.com/problems/number-complement/)
- 練習二進制思想
- [342. 4的冪](https://leetcode-cn.com/problems/power-of-four/)
- [318. 最大單詞長度乘積](https://leetcode-cn.com/problems/maximum-product-of-word-lengths/)
- [338. 比特位計數](https://leetcode-cn.com/problems/counting-bits/)
- 一些補充
- 劍指 Offer 56 - I. 數組中數字出現的次數
技巧
位運算的一些基礎技巧:
x ^ 0... = xx ^ 1... = ~xx ^ x = 0x & 0... = 0x & 1... = xx & x = xx | 0... = xx | 1... = 1...x | x = x
進階技巧:
n & (n-1) 可以去除n的位級表示中最低的一位:
如 n = 11110100 , n - 1 = 11110011
n & (n-1) = 11110000
n & (-n) 可以得到n的位級表示中最低的那一位:
如 n = 11110100 , 取負:-n = 00001100
n & (-n) = 00000100
練習位運算
461. 漢明距離
先異或運算,然后統計1個數:
class Solution {
public:int hammingDistance(int x, int y) {int XOR_result = x ^ y;int result = 0;while(XOR_result != 0){result += (XOR_result & 1);XOR_result = XOR_result >> 1;}return result;}
};
190. 顛倒二進制位
result不斷左移,最低位加上n的最低位,n不斷右移。
class Solution {
public:uint32_t reverseBits(uint32_t n) {uint32_t result = 0;for(int i = 0; i < 32; i++){result = result << 1;result += n & 1;n = n >> 1;}return result;}
};
136. 只出現一次的數字
思路一:哈希先掃一遍,然后再掃一遍,找到second為1的it。
class Solution {
public:int singleNumber(vector<int>& nums) {unordered_map<int,int> umap;int result = 0;for(int i = 0; i < nums.size(); i++){umap[nums[i]]++;}for(auto it : umap){if(it.second == 1) return it.first;}return 0;}
};
思路二:利用 x ∧ x = 0 和 x ∧ 0 = x 的特點,將數組內所有的數字進行按位異或。出現兩次
的所有數字按位異或的結果是 0,0 與出現一次的數字異或可以得到這個數字本身。神仙思路!
class Solution {
public:int singleNumber(vector<int>& nums) {int result = 0;for(int num : nums)result = num ^ result;return result;}
};
260. 只出現一次的數字 III
這次是有兩個不同的元素,那么用位運算如何做呢?
覺得這個題解寫的很好,淺顯易懂。
某題解
編程時注意一下細節:
1、&的優先級比==低,所以需要加括號
2、用int作為res會報錯,需要用long
class Solution {
public:vector<int> singleNumber(vector<int>& nums) {long res = 0;for(int i = 0; i < nums.size(); i++){res = res ^ nums[i];}//此時res = a ^ b;//找出1的位級表示中最低的那一位,代表著a與b在這一位是不一樣的,假設a在這一位為0,b在這一位為0res = res & (-res);int a = 0;int b = 0;for(int i = 0; i < nums.size(); i++){if((nums[i] & res) == 0) //不可能b,而且除了a其他符合要求的都是重復的a = nums[i] ^ a;else b = nums[i] ^ b;}return {a,b};}
};
268. 丟失的數字
XOR原理:x ^ x = 0; 0 ^ y =y;
兩個相同的數字XOR之后為0,如果這個數字只出現了一次,那么XOR之后還是本身。這一題本質上和上一題是一樣的。
一些細節,看注釋
class Solution {
public:int missingNumber(vector<int>& nums) {int n = nums.size();int result = n; //因為for循環中i不能為n,而我們必須遍歷[0,n],所以初值設置為nfor(int i = 0; i < n; i++) //遍歷nums數組,其丟失數在nums[] + [0..n]中只出現了一次{result = result ^ i ^ nums[i];}return result;}
};
693. 交替位二進制數
class Solution {
public:bool hasAlternatingBits(int n) {int prev_tail = n & 1;while(n){int now_tail = (n >> 1) & 1;if(now_tail == prev_tail) return false;prev_tail = now_tail;n = n >> 1;}return true;}
};
476. 數字的補數
思路一:一個一個來,注意倒序。
class Solution {
public:int findComplement(int n) {int result = 0;vector<int> res;while(n){res.emplace_back(!(n & 1));n = n >> 1;}for(int i = res.size() - 1; i >=0; i--){result = (result << 1) | res[i];}return result;}
};
當然也可以使用棧來做,思路一樣的:
class Solution {
public:int findComplement(int n) {int result = 0;stack<int> res;while(n){res.push(!(n&1));n >>= 1;}while(!res.empty()){result = (result << 1) | res.top();res.pop();}return result;}
};
思路二:使用異或
舉例:
5的二進制是:0101,7的二進制是: 0111,它們的抑或為:0010,去掉前導零位即為取反。
再來一個例子,假設a為1110 0101,b為1111 1111,a^b = 0001 1010是a的取反。也就是說二進制位數與num相同,且全為1的數tmp與num的抑或即為所求。
class Solution {
public:int findComplement(int n) {int tmp=0;int tmp_n = n;while(n){tmp = (tmp << 1) | 1;n >>= 1;}return tmp ^ tmp_n;}
};
練習二進制思想
342. 4的冪
先考慮2的次方:
如果n為2的整數次方,那么它的二進制一定是0...1...0
的形式;那么n-1的二進制應該是0..011...1
的形勢。這兩個數按位求與的結果一定為0。
如果n為4的整數次方,它一定為2的偶數冪。所以4的冪與二進制數(1010101010…10)相與會得到0.
int 為32位,每4位1010對應16進制a,所以應該為0xaaaaaaaa;
注意&與==的優先級。
class Solution {
public:bool isPowerOfFour(int n) {return ( n > 0 && (n & n-1) == 0 && (n & 0xaaaaaaaa) == 0);}
};
318. 最大單詞長度乘積
怎樣快速判斷兩個字母串是否含有重復數字呢?可以為每個字母串建立一個長度為26的二進制數字,每個位置表示是否存在該字母。如果兩個字母串含有重復數字,那它們的二進制表示的按位與不為0。同時,我們可以建立一個哈希表來存儲字母串(在數組的位置)到二進制數字的映射關系,方便查找調用。
class Solution {
public:int maxProduct(vector<string>& words) {unordered_map<int,int> hashmap; //使用哈希表來存儲(位掩碼 -> 單詞長度)int ans = 0;for(auto word : words) //遍歷每個string{int mask = 0; //位掩碼int size = word.size(); //單詞長度for(auto c : word) //encode位掩碼mask = mask | (1 << (c-'a'));hashmap[mask] = max(hashmap[mask],size);//如果掩碼相同,存儲更長的字符串(說明有些字符重復)//對比當前單詞與之前的所有單詞,無重復字符,且長度乘積大于ans則更新ansfor(auto it : hashmap){if((mask & it.first)== 0) //如果無重復字符{ans = max(ans,size*it.second);}}}return ans;}
};
338. 比特位計數
利用dp+位運算。
定義一個數組dp,dp[i]表示數字i的二進制含有1的個數。
對于第i個數字,如果它二進制的最后一位為1,那么它含有1的個數則為dp[i] = dp[i-1]+1;
如果它二進制的最后一位為0,那么它含有1的個數和其右移結果相同,即dp[i] = dp[i>>1]
class Solution {
public:vector<int> countBits(int num) {vector<int> dp(num+1,0);for(int i = 1; i <= num; i++){if((i & 1) == 1)dp[i] = dp[i-1] + 1;else dp[i] = dp[i>>1];}return dp;}
};
一些補充
2021.7.4
劍指 Offer 56 - I. 數組中數字出現的次數
一個整型數組 nums 里除兩個數字之外,其他數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。要求時間復雜度是O(n),空間復雜度是O(1)。
示例 1:
輸入:nums = [4,1,4,6]
輸出:[1,6] 或 [6,1]
示例 2:
輸入:nums = [1,2,10,4,1,4,3,3]
輸出:[2,10] 或 [10,2]
限制:
2 <= nums.length <= 10000
兩個只出現依次的數字位x,y
x和y二進制至少有一位不同,根據此位可以將nums劃分成分別包含x和y的兩個子數組。
分別對兩個子數組遍歷執行異或操作,可以得到兩個只出現一次的數字x,y。
1、遍歷nums數組執行異或,得到結果 x^y
2、x^y某二進制位為1,則x和y的此二進制位一定不同。
初始化一個輔助變量m = 1,從右向左循環判斷,可以得到x^y的首位1,記錄在m中。
3、拆分nums為兩個子數組
4、分別遍歷兩個子數組執行異或
for(num : nums)
if(num & m == 1) x ^= num;
else y ^= num;
return x,y;
最終代碼:
vector<int> singleNumbers(vector<int>& nums)
{int ret = 0;for(int n : nums)ret ^= n; // x^yint div = 1;while((ret & div) == 0)div <= 1;int x = 0, y = 0;for(int n : nums){if(div & n == 0)a ^= n;else b ^= n;}return vector<int> {x,y};
}