劍指offer之二進制中1的個數(shù)

1 問題

實現(xiàn)一個函數(shù),輸入一個函數(shù),輸出該二進制數(shù)據(jù)中1的個數(shù)。例如9表示二進制數(shù)據(jù)1001,有2位是1,因此輸入9,該函數(shù)會輸出2。

 
2 分析

我們先了解下計算機里面位運算,有5種

1)& 這個是與操作,規(guī)律如下

1 & 1 = 1     1 & 0 = 0     0 & 1= 0    0 & 0 = 0

2)| 這個是或運算,規(guī)律如下

1 | 1 = 1     1 | 0 = 1     0 | 1= 1    0 | 0 = 0

3)^ 異或運算,規(guī)律如下

1 ^ 1 = 0     1 ^ 0 = 1      0 ^ 1 = 1     0 ^ 0 = 0

4) 左移 m<<n 表示把m左移n位,在左移n位的時候,最左邊的n位丟棄,同時右邊補上n個0 比如

    00001010 << 2 = 00101000
     
    10001010 << 3 = 01010000

5) 右移 左移 m >> n 表示把m右移n位,在右移n位的時候,最右邊補上n個0 ,最左邊分2種情況,如果數(shù)字是一個無符號整形

則用0填補最左邊的n位,如果是一個有符號的數(shù)據(jù),則最左邊用數(shù)字的符號填補n個數(shù)據(jù)。如果是正數(shù),左邊補n個0,是負數(shù)左邊補n個1.

    00001010 >> 2 = 00000010
     
    10001010 >> 3 = 11110001


這里我們可以把原數(shù)據(jù)和1進行&操作,如果二進制數(shù)據(jù)尾巴進行&操作,如果包含1的話&1操作就是1,返之結果為0,然后我們把數(shù)據(jù)進行右移一位就行。

 
如果一個正數(shù)要除以2,我們效率最高的是把這個數(shù)據(jù)進行右移一位。

 
3 代碼實現(xiàn)

C++版本

    #include <stdio.h>
     
    /*
     *二進制數(shù)據(jù)里面包含數(shù)字1的個數(shù)
     */
    int containOneNumber(int value)
    {
        int count = 0;
        while (value != 0)
        {
            //這里是(value & 1)不是(value  & 0)
            if (value & 1)
                ++count;
            //這里是value = value >> 1,而不是value >> 1; 我們要用變量接收它
            //不然不管就只執(zhí)行了一次也就是value除以了2,所以導致死循環(huán)。
            value = value >> 1;
        }
        return count;
        
    }
     
    int main(void)
    {
        int result = containOneNumber(9);
        printf("result is %d\n", result);
        return 0;
    }

java版本

    public int containOneNumber(int value)
        {
            int count = 0;
            while (value != 0)
            {
                if ((value & 1) != 0)
                {
                    count++;
                }
                value = value >>> 1; //>>>就是java中的無符號右移
            }
            return count;
        }

我們知道java用 >>> 是無符號右移,右移的時候,所以最高位左邊都是0,如果這個數(shù)是負數(shù)的時候,右移的話最高位會補1,

C++版本就會變成死循環(huán)。

 
4 優(yōu)化
方法1:

n與1做與運算,判斷n的最低位是不是為1,接著把1左移一位得到2,再和n做與運算,就能判斷n的次低位是不是1….這樣反復左移

    int containOneNumber1(int n)
    {
        int flag = 1;
        int count = 0;
        while (flag != 0)
        {
            if ((flag & n) != 0)
            {
                count++;
            }
            flag = flag << 1;
        }
        return count;
    }

 
方法2:

把一個整數(shù)減去1,再和原整數(shù)做與運算,會把該整數(shù)最右邊一個1變成0,那么一個整數(shù)的二進制表示中有
多少個1,就可以進行多少次這樣的操作

    int containOneNumber1(int n)
    {
        int flag = 1;
        int count = 0;
        while (n != 0)
        {
            ++count;
            n =  n & (n - 1);
        }
        return count;
    }

 
5 總結

1) 把一個整數(shù)減去1,再和原整數(shù)做與運算,會把該整數(shù)最右邊一個1變成0,那么一個整數(shù)的二進制表示中有多少個1

2)n與1做與運算,判斷n的最低位是不是為1,接著把1左移一位得到2,再和n做與運算,就能判斷n的次低位是不是1….這樣反復左移

3) 如果是正整數(shù)的情況下,我們可以把正整數(shù)右移動和1進行&操作,然后再去統(tǒng)計。



作者:chen.yu
深信服三年半工作經(jīng)驗,目前就職游戲廠商,希望能和大家交流和學習,
微信公眾號:編程入門到禿頭 或掃描下面二維碼
零基礎入門進階人工智能(鏈接)