整數的二進位制中1的個數
題:輸入一個整數,輸出該數二進位制表示中1的個數。其中負數用補碼錶示。
思路1:對於輸入的整數n,判斷它的二進位制表示的最右邊是否為1,然後將n右移一位,直到n為0,就可以得出整數n的二進位制表示中1的個數。怎麼判斷二進位制表示的最右邊是否為1呢,拿n與1做與運算,因為1的二進位制表示為0001,所以,如果n的二進位制表示的最右邊為1,那麼與運算的結果就是1。
程式碼:
public int NumberOf1(int n) {
if(n == 0)
return 0;
int count = 0;
while(n != 0){
if ((n & 1) == 1)
count++;
n = n >> 1;
}
return count;
}
這種解法,看似很完美,但是當輸入的整數為負數的時候,程式就會陷入死迴圈。因為當輸入的數為負數時,當將n右移的時候,最高位補的就不是0了而是1,這樣n就一直不會等於0,也就是進入了死迴圈。
思路2:在上面的思路的基礎下,換一個方位來想。我們不動n,而是動和n做與運算的1。令1=i,我們在每一次做與運算後,讓i往左移1位,這樣就相當於輪詢n的二進位制表示的每一位。因為1不管怎麼向左移,它的二進位制表示裡始終就只有一個1。所以如果n與i相與的結果不為0或者說等於i,那麼就表示n的二進位制表示裡這一位上面是1。
程式碼:
public int NumberOf1(int n) {
if(n == 0)
return 0;
int count = 0;
int i = 1;
while(i != 0){
if((n & i) == i)
count++;
i = i << 1;
}
return count;
}
這種思路,就不會導致上面的死迴圈,因為我們根本沒動n,所以這段程式碼是可以通過的,但它不是最優的。
最優思路:如果整數n不為0,那麼n的二進位制表示裡至少有一位是1。如果我們把n減去1,那麼原來處在n的二進位制表示的最右邊的1就會變成0,原來在1後面的所有的0都會變成1。其餘的所有位將不受到影響。舉個例子:一個二進位制數1100,從右邊數起的第三位是處於最右邊的一個1。減去1後,第三位變成0,它後面的兩位0變成1,而前面的1保持不變,因此得到結果是1011。
可以發現減1的結果是把從最右邊一個1開始的所有位都取反了。這個時候如果我們再把原來的整數和減去1之後的結果做與運算,從原來整數最右邊一個1那一位開始所有位都會變成0。如1100&1011=1000。也就是說,把一個整數減去1,再和原整數做與運算,會把該整數最右邊一個1變成0。那麼一個整數的二進位制有多少個1,就可以進行多少次這樣的操作。
程式碼:
public int NumberOf1(int n) {
if(n == 0)
return 0;
int count = 0;
while(n != 0){
count++;
n = n & (n-1);
}
return count;
}
嘔心瀝血寫出來的,轉載請一定註明出處!