【剑指offer之二进制中1的个数】

【题目描述】:

请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。

【思路1】可能引起死循环的解法

这是一道很基本的考查二进制和位运算的面试题。题目不是很难,面试官提出问题之后,我们很快就能形成一个基本的思路:先判断整数二进制表示中最右边一位是不是1.接着把输入的整数右移一位,此时原来处于从右边数起的第二位被移到最右边了,在判断是否为1.这样每次移动一位,直到整个整数变成0为止。问题变成如何判断一个整数的最右边是否为1:不难想到,只需把整数和1做与运算看结果是否为0就知道了。如果一个整数与1做与运算的结果为1表示该整数最右边是1,否则为0.基于如此思路,代码如下:


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


面试官看了代码之后可能会问:把整数右移一位和把整数除以2在数学上是等价的,那上面的代码中可以把右移运算换成除2运算?答案是否定的。因为除法的效率比移位运算要低得多,在实际编程中应该尽可能地用移位运算符代替乘除法。

面试官接下来可能要问的第二个问题就是:上面的函数如果输入一个负数,比如0x80000000,运行的时候会发生什么情况?把负数0x80000000右移一位 的时候,并不是简单地把最高位的1移到第二位变成0x40000000,而是0xC0000000。这是因为移位前是负数,仍然要保持移位后是负数,因此移位后的最高位仍然为1,如果一直做右移运算,最终这个数字会变成0xFFFFFFF而陷入死循环。


【思路2】常规解法

为了避免死循环,我们可以不右移输入的数字n。首先把n和1做与运算,判断n的最低位是不是1,接着把1左移一位得到2 ,再和n做与运算,就能判断n的次第位是不是为1.。。

反复左移运算,每次都能判断n的其中一位是不是1。此种解法的次数等于整数二进制的位数,32位的整数需要循环32此,基于此思路,代码修改如下:


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


【思路3】

在分析这种算法之前,我们先来分析把一个数减去1的情况。如果一个整数不等于0,那么该整数的二进制表示中至少一位是1。先假设这个数的最右边一位为1,那么减去1时,
最后一位变成0而其他所有位都保持不变。也就是最后一位相当于做了取反操作,由1变成了0。
接下来假设最后一位不是1而是0的情况。如果该整数的二进制表示中最右边1位于第m位,那么减去1时,第m位由1变成0,而第m位之后的所有0都变成1,整数中第m位之前的所有位都保持不变。举个例子:
一个二进制数1100,它的第二位是从最右边数起的一个1。减去1后,变成1011
在前面的两种情况中,我们发现把一个整数减去1,都是把最右边的1变成0。如果它的右边还有0的话,所有的0都变成1,而它左边的所有位都保持不变。接下来我们把一个整数和它减去1的结果做位与运算,相当于把它最右边的1变成0。还是以前面的1100为例子,它减去1的结果是1011。二者做位与运算,得到的结果是1000,我们把1100最右边的1变成了0结果正好是1000。
把上面的分析总结:把一个整数减去1,再和原整数做与运算,会把该整数最右边的一个1
变成0。那么一个整数的二进制表示中有多少个1,就可进行多少次这样的操作。基于如此思路,代码如下:


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




牛客网版本:


class Solution {
public:
     int  NumberOf1(int n) {
         int ans=0;
         while(n){
             ++ans;
             n &=(n-1);
         }
         return ans;
     }
};


















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值