判斷32位無符號整數二進位制中1的個數
1、比較簡單和容易理解的方法就是逐位比較法:
#include <iostream> using namespace std; int findone(unsigned int n) { for(int i=0;n>0;n>>=1) i+=(n&1); return i; } int main(){ int n; cin>>n; cout<<findone(n)<<endl; return 0; }
這種方法的缺點是比較費時,時間長度取決於n的位數,時間複雜度O(n)。假如上千上萬位的話,每一位都要執行一遍,所用時間就很長了。
2、最直接的優化方法,其實就是空間換時間的思想:可以預建立一個表,存放了從0~2^32每個數中1的個數,用時去查一下表就知道了。但這樣顯然要耗費很多的空間(至少2^32/(256/32)=512MB,哈哈,正是一般記憶體大小)。於是需要再優化:存放0-255每個數中1的個數,然後分段查詢。如下面把32位數分為4段,每段一個位元組,所以有一個256大小供查詢的表:
char tOne[256]="\0\1\1\2\1\2……"; //後面省略 int findone(unsigned int n){ for(int i=0;n>0;n>>=8) //每次右移8位將32位分成四段 i+=tOne[n&255]; return i; }
3、上次在阿里雲筆試,碰到一題也是求一個整數中1的個數。
int
func(unsigned int
n){
int
count=0;
while (n>0){
n&=(n-1);
count++;
}
return
count;
}
|
比如n=10,二進位制為1010,count=2。
4、下面這種方法據說更快,但是我覺得不容易想出來。發現很多題目都可以用位運算來快速解決,可惜本人十分討厭使用它,總覺得在繞來繞去的,偉大的位運算...
int count_ones(unsigned a) { a = (a & 0x55555555) + ((a >> 1) & 0x55555555); a = (a & 0x33333333) + ((a >> 2) & 0x33333333); a = (a & 0x0f0f0f0f) + ((a >> 4) & 0x0f0f0f0f); a = (a & 0x00ff00ff) + ((a >> 8) & 0x00ff00ff); a = (a & 0x0000ffff) + ((a >> 16) & 0x0000ffff); return a; }
該程式碼的思路是這樣的:2位2位為一組,相加,看看有幾個1。再4位4位為一組,相加,看看有幾個1......
為了簡單說明,先看看8位的情形。相應地,函式裡面的語句變成。
x = (x & 0x55) + ((x >> 1) & 0x55); (1)
x = (x & 0x33) + ((x >> 2) & 0x33); (2)
x = (x & 0x0f) + ((x >> 4) & 0x0f); (3)
return x;
假設x=abcdefgh. 0x55=01010101
x & 0x55 = 0b0d0f0h. (x>>1) & 0x55 = 0a0c0e0g。相加。就可以知道2位2位一組1的個數。
比如x=11111111
x= (x & 0x55) + ((x >> 1) & 0x55); 之後x=10101010。你2位2位地看,10=2, 就是2 2 2 2, 就是說各組都是2個1。
比如x=00101001
x= (x & 0x55) + ((x >> 1) & 0x55); 之後x=00010101。你2位2位地看,就是0 1 1 1, 前1組只有0個1,後面的組都是1個1。
好啦。再來看。0x33=00110011。
x=abcdefgh.
x=(x & 0x33)+((x >> 2)&0x33); 相當於, 00ab00ef + 00cd00gh。
因為語句(1)之後。ab指示了頭兩位有多少個1,cd指示了下兩位有多少個1。相加00ab+00cd就指示前4位有多少個1。這樣就是4位4位為一組。注意這樣的分組,組與組之間永遠都不會產生進位的。正因為不會產生進位,才可以分開來看。
下面的過程都是一樣的,不再多說。8位,16位,32位都一樣。