面试题【8】二进制中 1 的个数

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

分析:通常为了解决这个问题,我们会将每一位与 1 进行 & 运算,然后将这个数 右移(>>) 一次。那么根据这种思想,我们写出下面这样的代码:

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

这种解法看上去没有问题,但我们考虑一下,如果输入的是一个负数,例如:0x8000 0000 ,将它右移一位时,并不是我们所想的简单的变成 0x4000 0000 ,而是 0xC000 0000。对于一个负数,右移一次,高位会补 1 ,这是因为移位前是一个负数,移位后要保持仍然是个负数。那么就会出现一个问题,当出现 0xFFFF FFFF时,上述代码就会是一个死循环。

为了克服这个问题,我们不再对 需要判断的数 进行移位,而是左移 1 。
什么意思?

例如:对于数 0x1101(二进制:0001 0001 0000 0001);
第一次 与 1 进行 & 运算时,count + 1,随后将 1 左移一次,变成 2 (10),其实就是判断次低位是否为 1。判断的次数取决于数据类型的大小,通常为了判断一个 int 类型的数字,我们会使用 unsigned int 进行判断,那么也就是要循环 32 次,代码如下:

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

那么这样的解法的确解决了死循环的问题,但是并不是最完美的。

下面介绍一种更加高效的解法:

讲述这种解法之前,先对 一个数 减 1 的情况进行分析。
例如:对一个二进制数 1010**10**,当 对它减 1 时,变为 1010**01**,将这两个数相与 得到 1010**00**;再减 1 时,变为 10**0111**,再与 10**1000** 相与 得到10**0000**。我们可以发现,当对一个数减 1 时,做右边的 1 会变为 0 ,并且最右边 1 的右边 所有的 0 会变为1 ,这样两者相 与 会使得 最右边的 1 变为 0 。
通过这种思想,我们可以实现一种更加简便高效的方法:

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

相关题目:
1.用一条语句判断一个整数是不是 2 的整数次方。例如:8, 16, 32,这些数都有一个共同特点就是写成二进制时,只有 1 位 为 1 ,其他都是 0 。

bool JudgePowerOf2(int n)
{
    int count = 0;
    while(n)
    {
        ++count;
        n = (n - 1) & n;

        if(count >= 2)
            return false;
    }
}

2.输入两个数 m 和 n ,计算需要改变 m 的二进制表示中多少位 才能得到 n 。比如说,10 的二进制 1010,13的二进制 1101 ,需要改变 1010 的 3 位才能得到 1101 。
如何解决这个问题呢?
其实,我们只要能求出两个数有多少位是不同的就可以了,那么通过 异或 ,我们可以将两个数中不同的位都变成 1 ,这样再使用上面的方法就可以了。

#include <iostream>

using namespace std;

void CountBinoryOf2(int num1, int num2)
{
    int num = num1 ^ num2;
    int count = 0;
    while (num)
    {
        ++count;
        num = (num - 1) & num;
    }
    cout << "需要改变" << count << "次" << endl;
}

int main()
{
    int number1, number2;
    cout << "请输入两个整数" << endl;
    cin >> number1 >> number2;

    CountBinoryOf2(number1, number2);

    return 0;
}

测试:100 与 17,需要 5 次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值