一、题目描述

二、解题思路

  • 解法1:逐步查看该数字的最低位,看是否为 1 1 1,然后把数字左移 1 1 1位。时间复杂度为 O ( 32 n ) O(32n) O(32n)
  • 解法2:将数字不断与 ( n − 1 ) (n-1) (n1)相与,每次计算完成后,只要数字不为 0 0 0,就说明存在 1 1 1 1 1 1

三、解题代码

解法1

class Solution {
   
public:
    int hammingWeight(uint32_t n) {
   
        int count = 0;
        while(n){
   
            if(n & 1)
                count++;
            n = n >> 1;
        }
        return count;
    }
};

解法2

class Solution {
   
public:
    int hammingWeight(uint32_t n) {
   
        int count = 0;
        while(n){
   
            count++;
            n &= (n - 1);
        }
        return count;
    }
};

三、测试程序

/******************************************************************* Copyright(c) 2016, Harry He All rights reserved. Distributed under the BSD license. (See accompanying file LICENSE.txt at https://github.com/zhedahht/CodingInterviewChinese2/blob/master/LICENSE.txt) *******************************************************************/

//==================================================================
// 《剑指Offer——名企面试官精讲典型编程题》代码
// 作者:何海涛
//==================================================================

// 面试题15:二进制中1的个数
// 题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如
// 把9表示成二进制是1001,有2位是1。因此如果输入9,该函数输出2。

#include <cstdio>
/* int NumberOf1_Solution1(int n) { int count = 0; unsigned int flag = 1; while (flag) { if (n & flag) count++; flag = flag << 1; } return count; } int NumberOf1_Solution2(int n) { int count = 0; while (n) { ++count; n = (n - 1) & n; } return count; } */
int NumberOf1_Solution1(int n)
{
   
    int count = 0;
    while (n)
    {
   
        count++;
        n &= (n - 1);
    }
    return count;
}
int NumberOf1_Solution2(int n)
{
   
    int count = 0;
    while (n)
    {
   
        if (n & 1)
            count++;
        n = n >> 1;
    }

    return count;
}
// ====================测试代码====================
void Test(int number, unsigned int expected)
{
   
    int actual = NumberOf1_Solution1(number);
    if (actual == expected)
        printf("Solution1: Test for %p passed.\n", number);
    else
        printf("Solution1: Test for %p failed.\n", number);

    actual = NumberOf1_Solution2(number);
    if (actual == expected)
        printf("Solution2: Test for %p passed.\n", number);
    else
        printf("Solution2: Test for %p failed.\n", number);

    printf("\n");
}

int main(int argc, char *argv[])
{
   
    // 输入0,期待的输出是0
    Test(0, 0);

    // 输入1,期待的输出是1
    Test(1, 1);

    // 输入10,期待的输出是2
    Test(10, 2);

    // 输入0x7FFFFFFF,期待的输出是31
    Test(0x7FFFFFFF, 31);

    // 输入0xFFFFFFFF(负数),期待的输出是32
    Test(0xFFFFFFFF, 32);

    // 输入0x80000000(负数),期待的输出是1
    Test(0x80000000, 1);

    return 0;
}

四、运行结果

Solution1: Test for 0000000000000000 passed.
Solution2: Test for 0000000000000000 passed.

Solution1: Test for 0000000000000001 passed.
Solution2: Test for 0000000000000001 passed.

Solution1: Test for 000000000000000a passed.
Solution2: Test for 000000000000000a passed.

Solution1: Test for 000000007fffffff passed.
Solution2: Test for 000000007fffffff passed.

Solution1: Test for 00000000ffffffff passed.
Solution2: Test for 00000000ffffffff passed.

Solution1: Test for 0000000080000000 passed.
Solution2: Test for 0000000080000000 passed.

五、复盘

我发现LeetCode上的都是CodeInterview上的弱化版本,实际上CodeInterview里要考虑数据为负数的情况
那么我的第二种解法就不好使了,比如数据为0x80000000,这可是个负数
因为int型数据正数为原码,负数为补码。
最高位是符号位,正数最高位是0,负数最高位为1
那么对于负数1111 1111 1111 1111 1111 1111 1111 1111:其值为 − 1 ‬ -1‬ 1
那么对于负数1000 0000 0000 0000 0000 0000 0000 0001:其值为 − 2 , 147 , 483 , 647 ‬ -2,147,483,647‬ 2,147,483,647
但是别忘了 + 0 = = − 0 +0 == -0 +0==0,所以多出了一个数据,那么就使用 − 0 -0 01000 0000 0000 0000 0000 0000 0000 00000x8000000来表示更大的数字,即 − 2 , 147 , 483 , 648 -2,147,483,648 2,147,483,648
那么使用解法1来计算0x8000000,将会卡在 − 1 -1 1然后进入死循环
为什么呢,因为负数在内存中以补码(补码:符号位不变,其余位按照原码取反加1)形式保存,右移时保证符号位不变,其余位向右移动到X位,在移动的过程中,高位补1
那么对于1000 0000 0000 0000 0000 0000 0000 0000,势必最后变成1111 1111 1111 1111 1111 1111 1111 1111,这个数字是-1,于是再怎么位移都是-1,死循环,所以跳出循环的条件还要加上是不是等于-1
综上,代码优化为:

int NumberOf1_Solution2(int n)
{
   
    int count = 0;
    if (n < 0)
    {
   
        if (n == -1)
            return 32;
        count++;
    }

    while (n && n != -1)
    {
   
        if (n & 1)
            count++;
        n = n >> 1;
    }
    return count;
}