统计二进制中1的个数

这是一道考察二进制和位运算的题目,最简单想法就是判断右边最后一位是不是1,接着右移一位判断最右边以为是不是1,右移到这个数变为0为止。
判断最右边一位是不是1,可以让这个数和整数1做&运算,真则为1,假则为0。
代码如下:
int Numof1(int n){
int count = 0;
while(n){
if(n &1 ) count++;
n=n >> 1;
}
return count
}
以上代码中n=n>>1;可不可以换成n/=2;呢?虽然一个整数/2和>>1是等价的,但是除法的效率比移位运算的效率低得多,在实际编程中应尽可能的用移位运算代替乘除法。
同时以上代码还存在一个问题,就是当n为负数的时候,n=n>>1;并不是简单的把n右移一位,而是在保证他的符号不变的情况下右移一位,也就是要保证负数右移一位之后还是个负数,最高位会设为1。把负数0x800000000右移一位不会变成0x40000000,而是变为0xC0000000。如果一直做右移运算,最终这个数字会变成0xFFFFFFFF而陷入死循环。
为了避免死循环,我们可以不右移整数n,而去选择左移1和整数n做&运算。
int Numof1(int n){
int count = 0;
unsigned int flag = 1;
while(flag){
if(n & flag) count++;
flag = flag << 1;
}
return count;
}
这个解法中循环的次数是n类型的二进制位数,int型需要循环32次。
而在以下算法,整数中有几个1就只需要循环几次。
在分析这种算法之前,我们先来分析把-一个数减去1的情况。如果一个整数不等于0,那么该整数的二进制表示中至少有一位是1。先假设这个数的最右边一位是1,那么减去1时,最后一位变成0而其他所有位都保持不变。也就是最后一位相当于做了取反操作,由1变成了0。
接下来假设最后一位不是1而是0的情况。如果该整数的:二进制表示中最右边1位于第m位,那么减去1时,第m位由1变成0,而第m位之.后的所有0都变成1,整数中第m位之前的所有位都保持不变。举个例子:
一个二进制数1100,它的第二位是从最右边数起的一个1。减去1后,第二位变成0,它后面的两位0变成1,而前面的1保持不变,因此得到的结果是1011。
在前面两种情况中,我们发现把-一个整数减去1,都是把最右边的1变成0。如果它的右边还有0的话,所有的0都变成1,而它左边所有位都保持不变。接下来我们把一个整数和它减去1的结果做位与运算,相当于把它最右边的1变成0。还是以前面的1100为例,它减去1的结果是1011。我们再把1100和1011做位与运算,得到的结果是1000。我们把1100最右边的1变成了0,结果刚好就是1000。
我们把上面的分析总结起来就是:把-一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。基于这种思路,我们可以写出新的代码:
int Numof1(int n){
int count = 0;
while(n){
count++;
n = (n - 1) & n;
}
return count;
}
本文探讨了如何有效地计算一个整数的二进制表示中1的个数。通过位运算和右移操作,提出了两种解决方案。第一种方法使用右移和位与操作,但不适用于负数;第二种方法利用减法和位与操作,避免了负数问题,且循环次数等于1的个数。这种方法更高效,适合实际编程应用。
527

被折叠的 条评论
为什么被折叠?



