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

测试用例1:

测试用例2:

测试用例3:

首先来分析题目,要统计二进制中1的个数,那么我们首先想到的是 要将此数的每个比特位都要遍历一遍,然后判断为1count++,最后返回count。就写出如下代码

int hammingWeight(uint32_t n) {
        int count = 0;
        while(n)
        {
            if(n&1)
                count++;
            
            //此处移位时,如果n为负数可能会导致死循环
            n >>= 1;
        }
        return count;
    }

上面的代码在统计无符号数时,结果是正确的,但是,在算负数时,可能会导致死循环。 此处我们采用的 位运算 来代替 除法运算,因为计算机中除法的效率要比比移位运算要低得多。我们在实际编程中要尽量地用移位运算符代替乘除法。 再者我们来讨论一下测试用例为负数。右移运算符的特点是,右移n位的时候,最右边的n位将被丢弃。在右移处理最左边位的情形比较复杂。如果数字是一个无符号值,则用0填补最左边的n位;如果数字为有符号数,则用数字的符号位填补最左边的n位。也就是说,如果数字原先是正数,则右移之后在最左边补n个0;如果数字原先是负数,则右移之后在最左边补n个1。 所以负数在进行移位时,最终由于最高位一直补1导致最终结果陷入死循环。故设计出上述代码是由问题的,我们可以对其进行调整。

  • 正确示范1:
int hammingWeight(uint32_t n) {
        int count = 0;
        unsigned int flag = 1;
        while(flag)
        {
            if(n&flag)
                count++;
            
            flag = flag << 1;
        }
        return count;
    }

为了避免死循环,此处我们不右移输入的数字n,而是把n和1 按位与 ,判断i的最低位是不是为1,接着把i左移1位,再和n做按位与运算。这样反复右移就能计算出n中二进制位1的个数。这种解法惟一的不足之处是无论n中二进制位有多少个1,都要进行32次循环。那么还能不能在优化一下呢,答案是肯定可以,来看一下最终比较完美的解答: 完美解答:

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

此处将位运算的灵活性体现到了机制,可以看出代码设计的非常巧妙精简。其精髓就在于此句 n = (n-1)&n 。大家可以自己体会一下此处的妙笔。