剑指offer11_二进制中1的个数_题解
二进制中1的个数
题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
示例1
输入
10返回值
2
分析
方案一:二进制移位法
将整数看成二进制,让掩码mask从右往左与n的每一位进行&操作,若与操作结果为1,则n的当前位为1,bits加1,否则当前位为0,继续左移mask。
/**
时间复杂度:O(32)
空间复杂度:O(1)
**/
class Solution
{
public:
    int NumberOf1(int n){
        int bits = 0;
        int mask = 1;
        for(int i = 0; i < 32; i++){
            if((n & mask) != 0){
                bits++;
            }
            mask <<= 1;
        }
        return bits;
    }
};
思考
- 为什么不采用从左往右移位?
- 如果n本身为负数(负数在计算机中用补码表示)向右移位最高位补1。程序会产生死循环
 
 
class Solution
{
    int NumberOf1(int n)
    {
        int bits = 0;
        while (n != 0)
        {
            if ((n & 1) != 0)
            {
                bits++;
            }
            n >>= 1;
            cout << n << endl;
        }
        return bits;
    }
};
栗子:假设 \(n=-1\) ,则\(n\) 的原码和补码表示分别为
\[1000,0001\tag{原码}
\]
\[1111,1111\tag{补码}
\]
-1的补码右移结果还是\(1111,1111\),也就是说-1右移的结果还是-1
故n无法减为0,产生死循环
方案二:巧用 \(n\&(n-1)\)
\(n\&(n-1)\) 解析: 二进制数字 \(n\) 最右边的 \(1\) 变成 \(0\) ,其余不变。

/**
时间复杂度:O(n)
设 M 为二进制数字 n 中 1 的个数,则需循环 M 次(每轮消去一个 1 ),占用 O(M)。
空间复杂度:O(1)
**/
class Solution
{
public:
    int NumberOf1(int n){
        int sum = 0;
        while (n != 0){
            sum++;
            n &= (n - 1);
        }
        return sum;
    }
};
方案三:bitset
C++的 bitset 在 STL 的 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅占用1bit 空间。
class Solution
{
public:
    int NumberOf1(int n){
        bitset<32> bs(n);
        return bs.count();// 返回1的个数
    }
};

                
            
        
浙公网安备 33010602011771号