【剑指Offer-位运算】面试题15:二进制中1的个数
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路1
可以先判断该整数对应的二进制最右一位是不是1,判断方法为将整数的二进制和1做与运算(&),如果整数二进制的最右一位是1,则与运算结果为1,否则为0。将整数二进制最右一位进行判断后右移一位(>>),直至整数为0。但这种方法只能判断非负整数二进制中1的个数,因为负数二进制的第一位为1,当进行右移操作时,左边会补上1,最终造成死循环。
思路2
在思路1中是右移整数二进制&1进行判断,但这种方法会造成死循环。为了避免死循环,我们保持输入整数的二进制不变,对1左移(<<)后和整数二进制与操作进行判断,这样每次都能判断整数二进制中的一位,在这个算法中,循环的次数等于整数1的二进制位数,例如32位的整数需要循环32次。代码如下:
class Solution {
public:
int NumberOf1(int n) {
unsigned int flag=1; //要使用unsigned int
int cnt=0;
while(flag){
if(flag&n)
cnt++;
flag = flag<<1;
}
return cnt;
}
};
需要注意的是,1的类型要是unsigned int,不能是int。
思路3
思路2可以准确统计整数二进制中1的个数,但总要循环32次。在思路3中,整数二进制中包含多少个1,就循环多少次。首先有一个技巧:把一个整数减去1后,再和原来的整数做与运算,得到的结果就相当于把原来整数二进制最靠右一位的1转为0。例如,整数二进制可以分为两种情况,最靠右一位的1就是最右一位(如1011)和最靠右一位的1不是最后一位(如1010)。最靠右一位的1就是最右一位,假设为1101,则1011-1=1100,1011&1100=1010,1011最右一位变成了1;当最靠右一位的1不是最后一位,假设为1010,此时1010-1=1001,1010&1001=1000,最靠右一位的1(从左往右第3位)变成了1。这样,整数二进制中有多少位1,就可以进行多少次上面的操作(因为每次都把最靠右一位的1变成0,直到所有的1都变成0,那么整数值也为0,即为循环终止条件)。代码如下:
class Solution {
public:
int NumberOf1(int n) {
int cnt=0;
while(n){
n = (n-1)&n;
cnt++;
}
return cnt;
}
};
总结
把一个整数减去1后,再和原来的整数做与运算,得到的结果就相当于把原来整数二进制最靠右一位的1转为0。