问题描述
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
进阶:
如果多次调用这个函数,你将如何优化你的算法?
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
实现(Java):
解题思路1:位移运算和位取反运算符
本题可以使用两种运算符,右移和位取反运算符
如果输入的是正数,则可以直接用右移,我们每次可以判断数值的最右边一位是0还是1,即使用模2除法来判断能否整除2,如果是1,计数器count++。并且在每次循环判断的过程中,我们还需要将数值右移一位,从而继续判断左边的位的1的个数
如果输入的是负数,我们可以使用位取反运算符,将每位按位取反,对于Java来说,能够保证最高位符号位是0,此时就转变成了正数,然后按照求正数位1的个数来求出位1的个数,最后需要用32减去得到的这个结果,从而求得负数位1的个数。
代码实现
public int hammingWeight(int n) {
int x=n;
int count=0; //计数器count
while(x>0){
//如果x是正数
if(x%2==1){
//判断最右边的位是否是1
count++;
}
x=x>>1; //右移1位,接着判断左边的位
}
if(x<0){
x=~x; //按位取反
while(x>0){
if(x%2==1){
count++;
}
x=x>>1;
}
count=32-count; //最终还是要求出负数位1的个数
}
return count;
}
时间复杂度和空间复杂度均为O(1)
解题思路2:循环和位移动
这个方法比较直接。我们遍历数字的 32 位。如果某一位是 1 ,将计数器加一。
我们使用 位掩码 来检查数字的第 i位。一开始,掩码 m=1 因为 1 的二进制表示是
0000 0000 0000 0000 0000 0000 0000 0001
显然,任何数字跟掩码 1 进行逻辑与运算,都可以让我们获得这个数字的最低位。检查下一位时,我们将掩码左移一位。
0000 0000 0000 0000 0000 0000 0000 0010
并重复此过程。
代码实现
public int hammingWeight(int n) {
int x=n;
int count=0;
int mask=1;
for(int i=0;i<32;i++){
if((mask & x)!=0){
count++;
}
mask=mask<<1;
}
return count;
}