二进制中1的个数

##题目描述
        输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。


####方法一:
        最直观的方法,就是把这个数转变为二进制,然后判断最小位位是否为1,然后把数字往右移一位,这样右边第二位就变成了右边第一位,再继续判断,直到最后二进制数等于0。
因此不难得到以下代码:

private static int NumberOf1_One(int n)
{
    int count = 0;
    while(n != 0)
    {
	    if ((n & 1) == 1)
	    {
	        count++;
	    }
        n = n >> 1;
	}
    return count;
}

        但是这样会有一个问题就是,当我们判断负数的时候,因为符号位为1,当我们右移的时候,左边填充的将是1,这个时候,将可能陷入死循环,因此我们需要更优的算法。


####方法二:
        把数字往右移会出现死循环,那么可以考虑往左移,于是定义一个flag,从最小位开始,没判断一次,就往左移一位,直到flag与数字做与操作为0,说明数字的最大位都已经判断完了,因此结束循环,于是有下列代码:

//num&1:num的最后一位与1做与运算
private static int NumberOf1_Two(int n)
{
    int count = 0;
    int flag = 1;
    while (flag != 0)
    {
        if ((n & flag) != 0)
            count++;

        flag = flag << 1;
    }
    return count;
}

以上,就通过位运算得到了可以解决该问题的办法,那么接下来再思考一下,是否还有更优的算法呢?上述算法,需要把二进制的每一位都判断一遍,才能得到结果,那么有没有只判断1的算法呢?


####方法三:
        如果一个整数不等于0,那么该整数的二进制表示中至少有一位是0。
        假设最后一位不是0,那么减去1,最后一位变成0而其他所有位都保持不变,也就是最后一位相当于做了取反操作,由1变成了0.
        假设最后一位是0,如果该整数的二进制表示中最右边的1位于第m位,那么减去1时,第m位由1变成0,而第m位之后的所有0都变成1,整数中第m位之前的所有位都保持不变。
         把一个整数和它减去1的结果做位与运算,相当于把它最右边的1变成0。
        所以,这种方法,只要这个整数有几个1,就做几次这样的操作。
        综上,可以得到下列代码:

private static int NumberOf1_Three(int n)
{
    int count = 0;
    while (n != 0)
    {
        count++;
        n = n & (n - 1);
    }
    return count;
}

####方法四:
        还有一个办法,就是通过把数字变成字符串,然后把字符串里的字符代替掉,把0全部转换为空,然后再输出该字符串的长度,该字符串的长度就是二进制里1的个数了。
        当然,也可以把该数字的二进制转换为字符串后,从头到尾遍历一遍,是1就加一次,也能得到正确的结果。这里就不多描述了。

public int NumberOf1_Four(int n)
{
    //用法:string valueString = Convert.ToString( value, radix );
    //value 整数值,
    //radix 为2,8,10,16 分别代表进制。
    return Convert.ToString(n, 2).Replace("0","").Length;
}

##拓展题
问题一:用一条语句判断一个整数是不是2的整数次方。
        其实这个问题可以参考上述的方法三,如果一个整数是2的整数次方,那么它的二进制表示中有且只有一位是1,而其他所有位都是0。因此,把这个整数减去1之后再和它自己做与运算,这个整数中唯一的1就变成0。

private static bool IsTwoPow(int n)
{
    if (n == 0 || n == 1 || n == -1)
        return false;
    n = n & (n - 1);
    if (n == 0)
        return true;

    return false;
}

问题二:输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n。
比如:10的二进制表示为1010,13的二进制表示为1101,需要改变1010中的3位才能得到1101。
        因为通过位运算的异或操作,我们可以得到两个数二进制的不同位有哪些(为1就是需要改变的),因此做完异或操作,再统计1的个数即可。
        我们可以分为两步解决这个方法:第一步,求这两个数的异或;第二步,统计异或结果中1的位数。

private static int CountDigit(int n,int m)
{
    int count = 0;

    int temp = n ^ m;
    while (temp != 0)
    {
        count++;
        temp = temp & (temp - 1);
    }

    return count;
}

总结
        把一个整数减去1之后再和原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0。很多二进制的问题都可以用这种思路去解决。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值