位操作的定義
所謂的位操作,是指按二進制逐位進行邏輯運算。常見的位運算包括:取反、位與、位或、位異或以及左移、右移。
在 C/C++中,基本的位運算符總結如下,其中運算符優先級爲從上到下遞減,且<<和>>優先級相同,如下表所示:
操作符 | 功能 | 用法 |
---|---|---|
~ | 取反 | ~var |
<< | 左移運算(相當於乘法) | var<<pos |
>> | 右移運算(相當於除法) | var>>pos |
& | 位與 | var1 & var2 |
^ | 位異或 | var1 ^ var2 |
| | 位或 | var1 | var2 |
需要注意的是,位運算符只能用在帶符號或無符號的 char、 short、int 與 long 類型上。
在實際應用中,建議用 unsigned 整型操作數,以免帶符號操作數因爲不同機器導致的結果不同:無符號數左移/右移默認移入的新位是 0。對於符號數,當最高位是 1(代表負數)時,有的機器認爲右移移入的新位是 1。此外,複雜的位運算建議都用括號強制計算順序,而不是依賴於優先級,這樣做可以增加可讀性並避免錯誤。
位操作的應用
最基本的操作包括獲取位(獲取第i位的值)、設置位(設置第i位爲1)和清除位(設置第i位爲0)。
- 獲取位可以利用&1:&(0x1 << i);
- 設置位可以利用 |1:| (0x1 << i);
- 清除位可以利用 &0:&(~(0x1 << i))
例子1:說明如下的代碼做什麼事情:(n & (n-1)) == 0.
分析:我們不難歸納得到 n &= (n-1)相當於 clear 最低的一位1,我們可以用這個方法統計一個整數的二進制表示中有多少個 1。
那麼( n & (n-1) ) == 0 意味着該整數的二進制表示中只有一個 1,即它是 2 的次方。事實上,如果不能立刻看出結果,不妨嘗試從 1 開始列舉 n 的值,看看有什麼規律。根據觀察到的結果再來倒推表達式的含義。
例子2:給定整數的一個數組,每個元素都出現了兩次,只有一個元素例外。請編寫一個函數找到那個單個的元素。
分析: 當遇到某些題目需要統計一個元素集中元素出現的次數,應該直覺反應使用哈希表, key 是元素, value 是出現的次數。
掃描整個數組建立哈希表,再次掃描表看哪個元素出現了一次。這樣做的時間複雜度爲 O(n+n)。
這種解法具有普適性,應該首先想到。但是對於這道特殊的題目,能不能做的更好?我們考慮兩個數如果相等,二進制表示有什麼特點?很明顯,當然是二進制表示每位比特都相等。能否通過某種二進制操作把兩個相同整數變成常數?答案是異或: 相同整數異或得 0。
如何把這個性質利用到本題?如果我們異或所有得元素,則出現兩次的數都相互抵消,最後留下的就是單獨的那個。
複雜度分析: 掃描數組一次,複雜度爲 O(n)。
參考解答:
int singleValue(vector<int> array) {
int value = 0;
for (int i = 0; i < array.size(); i++) {
value ^= array[i];
}
return value;
}