1、题目描述
【JZ11】输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
知识点:二进制,位运算
难度:☆
2、解题思路
2.1 暴力遍历
对于输入的整数 n ,和整数 1 进行“按位与”运算,如果结果为 1 ,说明 n 的最低位为 1 ,反之为 0 。然后 n 右移一位,即第1位变为第0位,如此反复32次即可统计出 1 的个数。
无论输入的整数多大,都只需要重复 32 次。
2.2 消一法
请看一个例子:n = 42 ,对应二进制为:0000 0000 0000 0000 0000 0000 0010 1010。
省略前面的0,看成 101010。
减一后,变为 101001。
然后原数和减一之后的数进行“按位与”运算:101010 & 101001 = 101000。
仔细观察这个结果和原数 101010 比较,发现最右边的 1 没了。
101000 - 1 = 100111
然后 101000 & 100111 = 100000。
发现 101000 和 1000000 相比,最右边的 1 也没了。
总结:一个数和它减一后的数进行“按位与”运算,会消去该数的最右边的 1 。
因此,我们再回头解决题目,算法步骤如下:
1、val = n , count = 0;
2、val = val & (val - 1),count ++;
3、如果 val 不为 0 ,重复第 2 步;
3、解题代码
3.1 暴力遍历
package pers.klb.jzoffer.medium;
/**
* @program: JzOffer2021
* @description: 二进制中1的个数(暴力遍历)
* @author: Meumax
* @create: 2020-07-13 08:54
**/
public class NumberOfOne {
public int NumberOf1(int n) {
int count = 0;
// int 类型占 4 个字节,总共 32 个二进制位
for (int i = 0; i < 32; i++) {
if ((n & 1) == 1) {
count++;
}
n >>= 1;
}
return count;
}
}
时间复杂度:O(1)
空间复杂度:O(1)
3.2 消一法
package pers.klb.jzoffer.medium;
/**
* @program: JzOffer2021
* @description: 二进制中1的个数(消一法)
* @author: Meumax
* @create: 2020-07-13 08:54
**/
public class NumberOfOne {
public int NumberOf1(int n) {
int val = n;
int count = 0;
while (val != 0) {
count++;
val = val & (val - 1);
}
return count;
}
}
时间复杂度:O(N) N为val中1的个数,N ≤ 32,比暴力遍历优化。
空间复杂度:O(1)
4、解题心得
本题难度很小,基本上属于送分题,笔试的时候直接遍历即可。
消一法的本质就是利用了“与运算”的性质,两个 1 则输出 1 ,其他情况输出 0 ,一个数减去 1 后,它的最右边的 1 变为 0 ,该 1 后面的 0 全变为 1 ,进行“与运算”后就可以消除这最右边的 0 。
记住这个小技巧,面试的时候能写出来就好过暴力遍历。