题目描述
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:
-
输入:00000000000000000000000000001011
-
输出:3
-
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
-
输入:11111111111111111111111111111101
-
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。
解题思路
思路一: 逐位判断
解法一:按位与
作者:jyd
按位与运算符 &
参加运算的两个数据,按二进制位进行与运算
- 运算规则:0&0=0;0&1=0;1&0=0;1&1=1;
根据与运算定义,设二进制数字n,则有:
-
若 n & 1 = 0,则二进制最后一位为0;
-
若n & 1 = 1 ,则二进制最后一位 为 1
可能会有一些小困惑,为什么9 & 1 = 1
,10 & 1 = 0
?数字在计算机存的是补码,而按位与是相同为1,不同为0。和1
与完之后,源数字的二进制位并没有发生变化(如果是|
就变了),因此,我们只需要逐位缩短判断即可!
根据以上特点,考虑以下循环判断
-
判断n最后一位是否为1,根据结果计数。
-
将n右移一位(本题要求把数字n看作无符号数,因此使用无符号右移操作)
算法流程
-
初始化数量统计变量res = 0
-
循环逐位判断:当n = 0时跳出
-
res += n & 1;
若n & 1 = 1,则统计数res加。 -
n >>= 1;
将二进制数字n无符号右移位(Java中无符号右移为>>>
)
-
-
返回统计数量res
public static int hammingWeight(int n) {
int count = 0;
while(n != 0) {
if((n & 1) != 0) {
count++;
}
n = n >>> 1;
}
return count;
}
解法二:字符比较
将数字转换为二进制的字符串,拿到每一位和字符1
作比较
-
读取字符串的每一位
charAt()
【无解】 -
转换为字符数组(浪费空间,空间复杂度为O(n))
踩坑:
将给定的二进制数00000000000000000000000000001011转换为字符串时,会出现问题
int n = 00000000000000000000000000001011;
String str = n + "";
打印str为:521。由于我比较菜,还不知道为什么是这样?知道的大佬告知一下哈!
思路二:巧用 n & (n−1)
作者:jyd (图片来自大佬的题解,为了能够看懂我搬来了)
- (n−1) 解析: 二进制数字 n 最右边的 1 变成 0 ,此 1 右边的 0 都变成 1
- n&(n−1) 解析: 二进制数字 n 最右边的 1 变成 0 ,其余不变
算法流程
- 初始化数量统计变量res
- 循环消去最右边的1:当=0时跳出
- res+=1:统计变量加1;
- n&=n-1:消去数字n最右边的1。
- 返回统计数量res
public static int hammingWeight(int n) {
int count = 0;
while(n != 0) {
count++;
n &= n - 1;
}
return count;
}
思路三:使用Integer类提供的方法
解法一:toBinaryString()
【toBinaryString()方法分析】
- 以二进制(基数 2)无符号整数形式返回一个整数参数的字符串表示形式。
- 如果参数为负,该无符号整数值为参数加上 2^32;否则等于该参数。
因为Java里的int是有符号的,在内存中没有正负之分,只有0/1,整数是用补码表示的
正数补码等于原码
负数的补码等于其绝对值的反码+1,正好等于自身+2^32(对于4字节的整型来说)
-1
的补码 就是 绝对值1
的反码(按位取反) `11111111 11111111 11111111 11111110
再+1
,等于
11111111 11111111 11111111 11111111
这样正好能把最高位为1的数字用来表示负数,而最高位为0的数字表示非负数
10000000 00000000 00000000 00000000 => -2147483648
11111111 11111111 11111111 11111111 => -1
00000000 00000000 00000000 00000000 => 0
00000000 00000000 00000000 00000001 => 1
01111111 11111111 11111111 11111111 => 2147483647
因此负数+2^32
之后的二进制串,就是该负数内存中准确的存储形式
此题只是针对无符号数
Integer.toBinaryString(n).replaceAll("0", "").length();
把一个10进制数转为32位的2进制数,将0替换为空字符返回
解法二:bitCount()
Integer.bitCount(n)
统计二进制数中 1 的个数
此方法的源码:
此方法的源码分析参考文章:Integer.bitCount()理解