可能引起死循环的解法
int numOf1(int n)
{
int cnt = 0;
while (n)
{
if (n & 1)
++cnt;
n >>= 1;
}
return cnt;
}
把整数右移一位和把整数除以 2 在数学上是等价的,那上面的代码可以把右移运算换成除以 2 吗?答案是否定的。因为除法的效率比移位运算低得多,在实际编程中应尽可能地用移位运算代替乘除法。
为什么说,上述程序可能引起死循环呢?
如果输入一个负数,如 0x80000000,运行的时候会发生什么情况?把负数 0x8000 0000 右移一位的时候,并不是简单地把最高位的 1 移到第二位变成 0x4000 0000,而是 0xC000 0000。这是因为移位前是个负数,仍然要保持移位后也是负数,因此移位后的最高位会设为1。因此如果做右移运算,最终这个数字会变成 0xffffffff 而陷入死循环。
常规解法
为了避免死循环,我们可以不右移 n,而是左移 1。
int numOf1(int n)
{
int cnt = 0;
unsigned flag = 1;
while (flag)
{
if (n & flag)
++cnt;
flag <<= 1;
}
return cnt;
}
这个解法中循环的次数等于整数二进制的位数(flag << 1 左移超出整数的表示范围),32位的整数需要循环32次。下面再介绍一种算法,有多少1就只循环几次。
能给面试官带来惊喜的解法
把一个数减去1(n-1),再和原整数做与运算((n-1)&n),就会把整数最右边一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
int numOf1(int n)
{
int cnt = 0;
while (n)
{
++cnt;
n &= n-1;
}
return cnt;
}