劍指Offer面試題:9.二進制中1的個數


一、題目:二進制中1的個數

題目:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1。因此如果輸入9,該函數輸出2。

二、可能引起死循環的解法

  一個基本的思路:先判斷整數二進制表示中最右邊一位是不是1。接着把輸入的整數右移一位,此時原來處於從右邊數起的第二位被移到最右邊了,再判斷是不是1。這樣每次移動一位,直到整個整數變成0為止。

  怎么判斷一個整數的最右邊是不是1:只要把整數和1做位與運算看結果是不是0就知道了

    public static int NumberOf1Solution1(int n)
    {
        int count = 0;

        while (n > 0)
        {
            if ((n & 1) == 1)
            {
                count++;
            }

            n = n >> 1;
        }

        return count;
    }

PS:右移運算符m>>n表示把m右移n位。右移n位的時候,最右邊的n位將被丟棄。如果數字原先是一個正數,則右移之后在最左邊補n個0;如果數字原先是負數,則右移之后在最左邊補n個1。例如下面對兩個八位二進制數進行右移操作:

00001010>>2=00000010

10001010>>3=11110001

那么,問題來了:上面的方法如果輸入一個負數,比如0x80000000,如果一直做右移運算,最終這個數字就會變成0xFFFFFFFF而陷入死循環

三、避免引起死循環的解法

  為了避免死循環,我們可以不右移輸入的數字i:

  (1)首先把i和1做與運算,判斷i的最低位是不是為1。

  (2)接着把1左移一位得到2,再和i做與運算,就能判斷i的次低位是不是1。

  (3)這樣反復左移,每次都能判斷i的其中一位是不是1。

    public static int NumberOf1Solution2(int n)
    {
        int count = 0;
        uint flag = 1;
        while (flag >= 1)
        {
            if ((n & flag) > 0)
            {
                count++;
            }

            flag = flag << 1;
        }

        return count;
    }

PS:這個解法中循環的次數等於整數二進制的位數,32位的整數需要循環32次。

四、高效新穎的解法

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

    public static int NumberOf1Solution3(int n)
    {
        int count = 0;

        while (n > 0)
        {
            count++;
            n = (n - 1) & n;
        }

        return count;
    }

PS:把一個整數減去1之后再和原來的整數做位與運算,得到的結果相當於是把整數的二進制表示中的最右邊一個1變成0。很多二進制的問題都可以用這個思路解決。

五、單元測試

5.1 測試用例

    // 輸入0,期待的輸出是0
    [TestMethod]
    public void NumberOfOneInBinaryTest1()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(0),0);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(0),0);
    }

    // 輸入1,期待的輸出是1
    [TestMethod]
    public void NumberOfOneInBinaryTest2()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(1), 1);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(1), 1);
    }

    // 輸入10,期待的輸出是2
    [TestMethod]
    public void NumberOfOneInBinaryTest3()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(10), 2);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(10), 2);
    }

    // 輸入0x7FFFFFFF,期待的輸出是31
    [TestMethod]
    public void NumberOfOneInBinaryTest4()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(0x7FFFFFFF), 31);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(0x7FFFFFFF), 31);
    }

    // 輸入0xFFFFFFFF(負數),期待的輸出是32
    [TestMethod]
    public void NumberOfOneInBinaryTest5()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(0xFFFFFFFF), 32);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(0xFFFFFFFF), 32);
    }

    // 輸入0x80000000(負數),期待的輸出是0
    [TestMethod]
    public void NumberOfOneInBinaryTest6()
    {
        Assert.AreEqual(BinaryHelper.NumberOf1Solution2(0x80000000), 0);
        Assert.AreEqual(BinaryHelper.NumberOf1Solution3(0x80000000), 0);
    }

5.2 測試結果

  (1)測試通過情況:

  (2)代碼覆蓋率:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM