程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 編程之美2.1——求二進制樹中1的個數

編程之美2.1——求二進制樹中1的個數

編輯:C++入門知識

問題:
對一個4字節的無符號整形變量,求其二進制表示中“1”的個數,要求算法的執行效率盡可能高。
 
解法一(位遍歷法):使用位操作,移位後來判斷是否有1存在,利用v&0x01和v>>=1。
 
解法二(1遍歷法):在每次判斷中只與1的個數進行判斷,利用v&=v-1。
 
解法三(二分累加法):依次連續兩位相加,連續四位相加,連續八位相加。以上操作得到4個八位數,每個八位數的值表示8個數位之和(也就是這8位中1的個數),然後將每個八位數與緊接著前面的八位數相加,仍然得到4個八位數,但每個八位數的值表示16個數位之和(也就是這16位中1的個數),最後將每個八位數與前面再前面的一個八位數相加,最後取第一個八位數中前6位的值(這個值最多能表示63,大於1的最多個數32)。
   1:  int Count(unsigned x) {  
   2:     x = x - ((x >> 1) & 0x55555555);    // 也可改為(x + (x >> 1)) & 0x55555555;   
   3:     x = (x +(x >> 2))& 0x33333333;   
   4:     x = (x + (x >> 4)) & 0x0F0F0F0F;   
   5:     x = x + (x >> 8);   
   6:     x = x + (x >> 16);   
   7:     return x & 0x0000003F;   
   8:  }
將第2行的x看成16個連續的2個二進制數位ab,a和b分別表示1個二進制數,0x55555555在每連續的2個二進制數位上都是01,則ab-(ab>>1)&01=ab-a=2*a+b-a=a+b表示x的連續2個二進制數位之和。
將第3行的x看成8個連續的4個二進制數位ab,a和b分別表示2個二進制數,且值分別是x的對應2個二進制數位之和,0x33333333在每連續的4個二進制數位上都是0011,則ab&0011+(ab>>2)&0011=b+a=a+b表示x的連續4個二進制數位之和。
將第4行的x看成4個連續的8個二進制數位ab,a和b分別表示4個二進制數,且值分別是x的對應4個二進制數位之和,0x0F0F0F0F在每連續的8個二進制數位上都是00001111,則ab&00001111+(ab>>2)&00001111=b+a=a+b表示x的連續8個二進制數位之和。
 將第5行的x看成4個連續的8個二進制數位abcd,a,b,c和d分別表示8個二進制數,且值分別是x的對應8個二進制數位之和,x+(x>>8)=abcd+abc=a(a+b)(b+c)(c+d)。繼續第6行,x+(x>>16)=a(a+b)(b+c)(c+d)+a(a+b)=a(a+b)(a+b+c)(a+b+c+d)。最後8個二進制數(a+b+c+d)就是x的32個二進制數位之和。www.2cto.com
 
解法四(累加取余法):首先將連續三位相加,再將連續的6位相加,得到5個數(每個數是連續的6個二進制數位之和)+1個數(剩余的2個二進制數位之和),最後取余得到32個二進制數位之和。

1: int BitCount(unsigned int u)
    {
2:       unsigned int uCount;
3:       uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
4:       return ((uCount + (uCount >> 3)) & 030707070707) % 63;
    }
將第2行的u看成11個連續的3個二進制數位abc,a,b和c分別表示1個二進制數,033333333333在每連續的3個二進制數位上都是011,&上它會去掉這3個二進制數位上的最高位,011111111111在每連續的3個二進制數位上都是001,&上它只保留這3個二進制數位上的最低位,在這裡主要是保證前面的數位在右移後不會對該3個二進制數位的加減產生影響,則uCount在連續的3個二進制上的運算是abc-ab-a=4a+2b+c-2a-b-a=a+b+c,表示u的該3個二進制數位之和。
將第3行的uCount看成6個連續的6個二進制數位ab,a和b分別表示3個二進制數,且值分別是u的對應3個二進制數位之和,030707070707在每連續的6個二進制數位上都是000111,則ab & 000111+(ab>> 3) & 000111= b+a。在對結果取余之前,該結果可看成6個連續的6個二進制數位abcdef,a,b,c,d,e和f分別表示連續的6個二進制數,且其值是u的對應該6個二進制數位之和,該結果=f+64e+64^2d+64^3c+64^4b+64^5a,取余63後(乘法對取余運算有分配律(64*64*d)%63=((64%63)*(64%63)*(d%63))%63=d%63),得a+b+c+d+f,即u的32個二進制數位之和,且a+b+c+d+f<=32<63,取余操作對最後結果無影響,但如果是64位的數據就會有影響。
 

作者:linyunzju

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved