题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
思路一:
1、对于任意一个32位二进制表示的数n,现取n = xxxxxx01100 为例子,显然 n-1 = xxxxxx01011,则 n&(n-1) = xxxxxx01000;
2、上述例子想说的是,n&(n-1) 这一式子得到的是 n 的最低位的 1 变为 0 后的值。
3、令 n 为循环进行条件, n = n&(n-1),当循环结束时,循环进行的次数就是 1 的总数。
int NumberOf1(int n) {
int cnt = 0;
while(n) {
cnt++;
n = n & (n - 1);
}
return cnt;
}
思路二: 这是在一道题目中看到做法。
1、对于某个二进制数,如 n = 1011,取 n = n&(0x5) +((n>>1) & 0x5) = 0001 + 0101 = 0110;
(1)这个算式的意义是将n的相邻的两位相加,得到的结果也以二进制形式记录下来。
(2)由于二进制不是0就是1,所以得到的结果中,每相邻的两位组成的数值都代表着原先的n在这个位置上1的数量。
(3)例如上式的结果 0110,代表 n 的第1和第2位上( 11 部分)1的数量为 10 = 2;第3和第4位上( 10 部分)1的数量为 01 = 1。
(4)最好自己算一算,看看结果是怎么来的。
2、对于上式结果 n = 0110,取 n = n&(0x3) +((n>>2) & 0x3) = 0010 + 0001 = 0011;
(1)这个算式的意义是将n的相邻的两位一组,然后相邻的两组相加,得到的结果也以二进制形式记录下来
(2)由于 0110 中每相邻的两位组成的数值都代表着原先的n在这个位置上1的数量,所以得到的结果中,每相邻的四位组成的数值都代表着原先的n在这个位置上1的数量。
(3)例如上式的结果 0011,代表 n 的第1、第2、第3和第4位上( 整个 1011) 1的数量为 0011 = 3。
由于 1011 本身也只有四位,所以到这里就算好了,但对于 32 位的数还需要 相邻的四位一组,然后相邻的两组相加、八位、直到十六位。
代码中的 int n = n1 & 0x7fffffff;主要是针对负数,将符号位的 1 改为 0 ,最后输出时还要+1。
但在编译器上跑,不用这一段也能出正确结果,也就是说不用考虑负数,但牛客上面会出错…原因不明。
int NumberOf1(int n1) {
int n = n1 & 0x7fffffff;
n = (n & 0x55555555) + ((n>>1) & 0x55555555);
n = (n & 0x33333333) + ((n>>2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n>>4) & 0x0f0f0f0f);
n = (n & 0x00ff00ff) + ((n>>8) & 0x00ff00ff);
n = (n & 0x0000ffff) + ((n>>16) & 0x0000ffff);
if(n1 < 0)
return n+1;
else
return n;
}