常見位運算
對于位運算:
&
:按位與,有0
則0
。
|
:按位或,有1
則1
。
^
:按位異或,相同為0
、不同為1
。(無進位相加)
~
:二進制位按位取反。
對于位運算的常見使用:
1. 判斷一個數n
,二進制中第x
位是0
還是1
例如一個數n
:01001101
,要判斷第4
位是0
還是1
(從低位數)
就可讓01001101
按位與上00001000
,這樣如果第4
位為0
,結果就是0
;如果第4
位為1
,結果就是1
。
所以,就可以判斷**
(n>>x) & 1
**,結果為1
就表示第x
為1
;結果為0
則表示第x
位為0
。
2. 將一個數n
,二進制第x
位修改為1
例如一個數n
:01100100
,要將第4
位修改為1
就可以讓01100100
按位或上00001000
,這里無論第4
位是0
還是1
,結果都是1
;而其他位異或上0
都不變(1 | 0 = 1
、0 | 0 = 0
)。
而00001000
只需讓1
左移3
位即可。
所以,修改二進制第
x
位1
,只需:n | (1<<x)
3. 將一個數n
,二進制第x
位修改為0
例如一個數n
:00101100
,將第4
位修改為0
就可以讓00101100
按位與上11110111
;這樣無論第4
位是什么,結果都為1
,而其他位按位與上1
都不變(1 & 1 = 1
、0 & 1 = 0
)
而11110111
,只需要讓1
左移4
位,然后按位取反即可。
4. 位圖
對于位圖,簡單來說就是使用一個bit
位來表示狀態(0
和1
)
5. 提取一個數n
二進制中最右側的1
要提取一個數
n
二進制中最右側的1
,只需要n & (-n)
即可。
例如:n
:01100100
,而-n
的補碼(原碼取反加一):10011011
(取反)、10011100
(加一)
通過觀察,取反加一,就是最右側的1
的右側不變,左側取反。(右側為0
),再按位與即可提取最右側的1
。
01100100 & 10011011
結果就等于00000100
;
6. 去掉一個數n
二進制中最右側的1
有去掉一個數
n
二進制中最右側的1
,只需要n & (n-1)
即可
例如n
: 01011000
,而n-1
: 01010111
通過觀察,n-1
本質就是讓n
最右側1
的右側取反(由全0
變全1
),左側不變;
再按位與,右側結果都為0
(和n
一樣);而左側沒有變化。
01011000 & 01010111
結果為01010000
。
7. 按位異或
對于按位異或,常見的使用:
n ^ n = 0
n ^ 0 = n
a ^ b ^ c = a ^ (b ^ c)
(支持交換律)
一、位1的個數
對于這道題,要獲取一個數n
二進制中設置位的個數(1
的個數)
而我們可以通過n & (n - 1)
的操作來去掉n
二進制中最后的1
,所以,就可以對n
一直進行n = n & (n - 1)
操作,直到n
為0
。
而n &= (n - 1)
的次數就是數n
二進制中1
的個數。
class Solution {
public:int hammingWeight(int n) {int ret = 0;while (n) {n &= (n - 1);ret++;}return ret;}
};
二、比特位計數
這道題,給定一個n
要求,返回一個數組ans
,ans
中存儲的是[0 , n]
之間每個數二進制形式中1
的個數。
簡單來說就是,獲取[0,n]
中每個數二進制中1
的個數。
class Solution {
public:vector<int> countBits(int n) {vector<int> ans;for (int i = 0; i <= n; i++) {int tmp = i;int cnt = 0;while (tmp) {tmp &= (tmp - 1);cnt++;}ans.push_back(cnt);}return ans;}
};
三、漢明距離
對于這道題,給兩個數x
、y
,要求x
和y
的漢明距離(二進制中不同位的數目)
簡單來說就是求x
與y
二進制中,不同bit
位置的數目。
解題思路:
我們知道
^
操作,相同為0
,不同為1
;所以,x ^ y
二進制中為1
就表示x
和y
該bit
為不相同。只需要判斷
x^y
的二進制中1
的個數即可。
class Solution {
public:int hammingDistance(int x, int y) {int tmp = x ^ y;int ret = 0;while (tmp) {tmp &= (tmp - 1);ret++;}return ret;}
};
四、只出現一次的數字
這道題,給定一個非空的數組nums
,其他有一個元素只出現了一次,其他元素都出現了兩次。
^
操作:
n ^ 0 = n
n ^ n = 0
這里只需要將nums
中所有元素都進行^
即可,最終的結果就是只出現一次的元素(其他所有元素都出現了兩次,^
結果為0
。
class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;for (auto& e : nums) {ret ^= e;}return ret;}
};
五、只出現一次的數字 III
這道題給定一個數組nums
,其中存在兩個元素只出現了一次,其他所有的元素都出現了兩次;
這里要我們返回這兩個主出現了一次的元素。
解法一:
使用
hash
表統計數組中的元素和出現的次數;最后找出只出現一次的兩個元素,然后返回。
解法二:
假設只出現的元素為
x
和y
,將數組中的所有元素進行^
,最終結果為:x ^ y
。
- 提取
x ^ y
二進制bit
位最低位的1
:lowbit
;- (
x ^ y
)該bit
位為1
,x
和y
該bit
位不相同(通過該bit
位將x
和y
區分開)- 再遍歷
nums
數組,lowbit
位1
的為一組,lowbit
位0
的為一組(x
和y
一定不在同一組)。- 獲取
x
和y
,然后返回
class Solution {
public:vector<int> singleNumber(vector<int>& nums) {int n = 0;for (auto& e : nums)n ^= e;int lowbit = (n == INT_MIN ? n : n & -n);vector<int> ret(2, 0);for (auto& e : nums) {if (e & lowbit) // 1ret[0] ^= e;elseret[1] ^= e;}return ret;}
};
我的博客即將同步至騰訊云開發者社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws