题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
解题思路
首先需要注意,负数的补码是其反码加1(符号位为1),将负数转化为相应正数的想法是不可行的,因为它们的二进制补码是不一样的(不仅是符号位不同)。
然后需要注意,每次判断该整数的最后一位是否为1,然后将该整数右移一位的想法也是不可行的。因为有符号数的移位是算术移位,而不是逻辑移位。这意味着,每次移位后的符号位都是不变的,所以对一个负数进行移位将永远不可能变成0。
要解决这个问题,有两种方法。第一种方法是定义一个无符号数flag,初始化为1,每次向左移动一位。这样,flag就每次都是一个第i位为1而其他位为0的整数。用这个flag与整数n进行位与,从而判断n的第i位是否是1。这样对flag移位而不是对n移位,就避免了对负数进行移位将导致的死循环问题。需要注意,只有令flag为无符号数,才可以通过逻辑移位使得flag=1000...0。
第二种方法,对于一个整数n,如果n不为0,则n&(n-1)将使得n最低位的1变为0,而高位保持不变。因此,当n不为0时,每次将n&(n-1)的结果赋给n,并加以记录,直到n变成0。这样,就可以记录n总共有几位1了。需要注意的是,当n为INT_MIN(1000.....0)时,n-1将得到INT_MAX(0111....1),n(n-1)=0,总共有1个1。因此,n=INT_MIN的情况也不用单独拿出来判断。
代码
class Solution {
public:
int NumberOf1(int n) {
//方法一
/*
int count = 0; //记录1的个数
unsigned int flag = 1; //用于查看1的个数,必须用无符号数
while (flag > 0) //移动flag而非n
{
if (n & flag) //按位与
count++;
flag <<= 1;
}
return count;
*/
//方法二
//if (n == INT_MIN) //最小值是1000...
//return 1;
int count = 0;
while (n != 0) //有负数
{
count++;
n = n & (n - 1); //将最低位1去掉
}
return count;
}
};