《剑指offer》--------二进制中1的个数

题目·

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

方法一

最容易想到的思路就是将二进制数进行右移依次与1相与,并记录下相与结果为1的次数,该次数就是二进制中1的个数,这对于正数来说是可以的,但是负数不可以 ,数值在计算机中就是以补码形式存储的,负数最高符号位为1,当进行移位时最高位会进行补位为1 所以移位会陷入死循环。
为避免上述失误出现 ,我们反其道而行之,将比较位1进行左移与目标数进行 相与比较,当相与为1时,计数器加一,相与为0时不加,直到标志位等于0时退出循环。这里等于0是什么意思?一开始我看参考答案是并没有明白一会贴出代码在进行解释。

代码(c++)

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
         int flag=1;
         while(flag!=0)
         {
             if((n&flag)!=0)
                 count++;
             flag=flag<<1;
         }
         return count;
          
     }
};

int 型是32位的
所以flag一开始表示为
00000000000000000000000000000001
左移一位后
00000000000000000000000000000010
以此类推
每左移一位与目标数组进行一次相与比较,如果为1计数器加一;
当flag左移31位时为
10000000000000000000000000000000
再一次进行左移时 flag值为多少?是的为0;
这里借鉴另一位博主的解释:
1<<50的结果是什么
int类型的数据占4个字节,一共32位,所以最多左移31位,那么后面的50如何处理呢
测试结果:1<<50 = 1<<18
50跟18相差了32,有人可能猜到了跟int类型占32位有关,也确实如此
根据java规范中描述 int a<<b, b这个操作数只能取二进制数的低五位(就是最后5位)
50的二进制表示00…00 000110010 后五位 10010就是18
b操作数取后五位,也可以理解成 b/32 取余数
所以 int a << b 可以理解成 int a << (b%32)

所以当移32位时flag为0,跳出循环,返回1的个数;
很明显,这种方法存在浪费现象,假如目标数组为1,那么flag不用移位就已经比较完成,但是它必须走完32位的位移才可以变为0跳出循环,有没有更好的方法呢,见方法二。

方法二

将二进制数进行减一操作,再与原数进行相与就能消掉最右位的1,每消掉一次进行一次计数,直到最高位也比较完毕为止,
当最后一位为0时,通过减一再相与,可以发现最右位的1变为0其余位置不变,例如1010 减一为1001,两者相与为1000;
当最后一位为1时,最右边的1也就是最后一位1变为0其余位不变,例如1011减一位1010,相与为1010;
它不必等到32位再结束,只要发现目标数组为0就说明我们已经把最高位的1消为0了,这是退出循环返回的值就为1的个数。

代码

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
       while(n!=0)
       {
           n=n&(n-1);
           count++;
       }
         return count;
     }
};
发布了47 篇原创文章 · 获赞 2 · 访问量 1456

猜你喜欢

转载自blog.csdn.net/weixin_42076938/article/details/103767357
今日推荐