涉及位元位操作的演算法題有很多,本文對其中常見的一些進行匯總,所有資源來自網路。
一、判斷一個正整數是否是2的整數次冪
判斷一個整數N是否是2的整數次冪,這是個很常見的問題。比如4、8等都是2的整數次冪,而6不是。
解法1)一個基本的解法是設定i=1開始,迴圈乘以2直到i>=n,然後判斷i是否等於n即可。
解法2)當然還有一個更好的方法,那就是觀察一個數位二進位表示,如n=101000,則我們可以發現n-1=100111。也就是說n=>n-1是將n最右邊的1變成了0,同時將n最右邊的1右邊的所有位元位由0變成了1。因此如果n & (n-1) == 0就可以判定數字n為2的整數次冪。(注意一個地方,n=0為特殊情況,需要額外考慮,因此最終的判定運算式為n && !(n & (n-1)) ).
bool powOf2(int n){ return n && !(n & (n-1));}
二、求一個整數二進位表示中1的個數
如給定N=6,則它的二進位表示為0110,所以二進位表示中1的個數為2。
解法1)一個自然的方法是將N和1按位與,然後將N除以2,直到N為0為止。該演算法代碼如下
int numOf1(int n){ int cnt = 0; while (n) { if (n & 1) ++cnt; n >>= 1; } return cnt;}
上面的代碼存在一個問題,那就是如果輸入為負數的話,會導致死迴圈,為瞭解決負數的問題,如果使用的是JAVA,可以直接使用無符號右移操作符>>>來解決,如果是在C/C++裡面,為了避免死迴圈,我們可以不右移輸入的數字i。首先i和1做與運算,判斷i的最低位是不是為1。接著把1左移一位得到2,再和i做與運算,就能判斷i的次高位是不是1……這樣反覆左移,每次都能判斷i的其中一位是不是1。基於此,我們得到如下代碼:
int numOf1_new(int n){ int cnt = 0; unsigned int flag = 1; while(flag){ if(n & flag) cnt ++; flag = flag << 1; } return cnt;}
解法2)一個更好的解法是採用第一個題中類似的思路,每次n&(n-1)就能把n中最右邊的1變為0,所以很容易求的1的總數目。
int numOf1_2(int x){ int cnt = 0; while (x != 0) { x = x & (x-1); cnt++; } return cnt;}
三、反轉一個不帶正負號的整數的所有位元位
給定一個不帶正負號的整數N,反轉該整數的所有位元位。例如有一個8位的數字01101001,則反轉後變成10010110,32位的不帶正負號的整數的反轉與之類似。
解法1)自然的解法就是參照字串反轉的演算法,假設不帶正負號的整數有n位,先將第0位和第n位交換,然後是第1位和第n-1位交換...注意一點就是交換兩個位是可以通過異或操作XOR來實現的。因為0 XOR 0 = 0, 1 XOR 1 = 0, 0 XOR 1 = 1, 1 XOR 0 = 1,使用1異或0/1會讓其值反轉。
typedef unsigned int uint;uint swapBits(uint x, uint i, uint j){ uint lo = ((x >> i) & 1); //取x的第i位的值 uint hi = ((x >> j) & 1); //取x的第j位的值 if (lo ^ hi) { x ^= ((1U << i) | (1U << j)); //如果第i位和第j位值不同,則交換i和j這兩個位的值,使用異或操作實現 } return x;}uint reverseXor(uint x){ uint n = sizeof(x) * 8; for (uint i = 0; i < n/2; i++) { x = swapBits(x, i, n-i-1); } return x;}
解法2)採用分治策略,首先交換數字x的相鄰位,然後再是2個位交換,然後是4個位交換...比如給定一個8位元01101010, 則首先交換相鄰位,變成10 01 01 01,然後交換相鄰的2個位,得到01 10 01 01,然後再是4個位交換,得到0101 0110。總的時間複雜度為O(lgN),其中N為整數的位元。下面給出一個反轉32位整數的代碼,如果整數是64位的可以以此類推。
uint reverseMask(uint x) { assert(sizeof(x) == 4); // special case: only works for 4 bytes (32 bits). x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); return x; }
,