剑指offer:二进制中1的个数

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

要求:

1、整数,包括边界值1、0x7FFFFFFF

2、负数,包括边界值0x80000000、0xFFFFFFFF

3、0

显然,本题的难点也就是表示负数的情况,因为计算机中是用二进制来表示的,而又是用二进制的补码来表示的,所以当出现负数的情况下,我们不可以将负数右移来达到我们的目的,但是如果是正数的情况下,我们是可以通过右移来完成的。

那么我们应该采取什么做法呢?

我们可以反过来想,既然我们不能移动负数的位数,我们可以用一个数向左移,这样就可以和原来数做比较了。

现在贴出远吗如下:

#include<stdio.h>

int binary_one(int n)
{
    int k=0,tag=1;
    while(tag)
    {
        if(n&tag)k++;
        tag<<=1;
    }
    return k;
}
int main()
{
    printf("请输入一个整数:\n");
    int n;
    scanf("%d",&n);
    printf("此整数二进制中有 %d 个 1\n",binary_one(n));
    return 0;
}

如图所示,函数binary_one就是通过tag=1作初始数据来左移的。每次左移都要和原始数值做&操作,这样可以得到1的个数。


PS:上面的程序还是优点小瑕疵的,因为当n为整数的时候,是不需要从低位一直比较到最高位的,而是应该从高位到低位,所以改进的程序如下,分为两种情况:

#include <stdio.h>
#include <stdlib.h>

int numberOf1(int n)
{
    int count=0;
    int tag=1;
    if(n==0)return 0;
    if(n>0)
    {
        while(n)
        {
            if(n&1)count++;
            n>>=1;
        }
    }
    else
    {
        while(tag)
        {
            if(tag&n)count++;
            tag<<=1;
        }
    }
    return count;
}
int main()
{
    printf("请输入一个整数:\n");
    int n;
    scanf("%d",&n);
    printf("整数 %d 二进制中有 %d 个1\n",n,numberOf1(n));
    return 0;
}

PS:下面介绍一种非常新奇的解法:

对于一个整数的二进制表示,如果这个整数的最右边一位是1,那么减去1时,最后一位变成0而其他所有位都保持不变,也就是相当于对最后一位取反操作。现在我们假设最后一位不是1而是0 的情况,该整数的二进制表示中最右边1位位于第m位,那么减去1时,第m位由1变成0,而第m位之后的所有0都变成1,整数中第m位之前的所有位都保持不变,然后我们使用   (n-1)&n,这样每次操作就可以消去最右边的1。

下面是次程序代码:

#include<stdio.h>

int numberOf11(int n)
{
    int count=0;
    while(n)
    {
        count++;
        n=(n-1)&n;
    }
    return count;
}
int main()
{
    printf("请输入一个整数:\n");
    int n;
    scanf("%d",&n);
    printf("此整数 %d 二进制有 %d 个1\n",n,numberOf11(n));
    return 0;
}

最后一种解法代码十分简洁,但是这种做法不易想到,希望能好好总结,提炼出一些普遍规律。


推广:

1、用一条语句判断一个整数是不是2个整数次方,一个整数如果是2的整数次方,那么它的二进制表示中有且只有一位是1,而其他所有的位都是0,所以这题实际上就是要求大家判断一个整数中是否只有一个1.

显然这题只要用上面的语句   n&(n-1)就行了


2、输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n,比较10的二进制表示为1010,13的二进制表示为1101,则需要改变1010中的3位才能得到1101.我们可以分为两步来解决这个问题:

(1)求这两个数的异或,

(2)统计异或结果中1的位数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值