位操作的應用

位操作的定義

所謂的位操作,是指按二進制逐位進行邏輯運算。常見的位運算包括:取反、位與、位或、位異或以及左移、右移。

在 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;
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章