编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
方法一:直接遍历数字的 32 位。如果某一位是 1 ,将计数器加一。
如何确定当前位的数字是1呢?
- 使用与(&)运算: 1&1=1 ,1&0=0
- 1 << 1 ,将1左移一位
num = 00000000000000000000000000001011
1 的二进制表示是
0000 0000 0000 0000 0000 0000 0000 0001
mask = 00000000000000000000000000000001
第一次比较:
两者最后一位都是1,所以1&1=1,计数加一
比较完后,就将1左移一位
0000 0000 0000 0000 0000 0000 0000 0010
mask <<= 1
mask = 00000000000000000000000000000010
第二次比较:
都是1,所以计数加一
比较完后,将1左移一位
0000 0000 0000 0000 0000 0000 0000 0100
mask <<= 1
mask = 00000000000000000000000000000100
第三次比较:
num第三位是0,所以不计数
……
以此类推,重复此过程,把num的32位都比较一遍,就能获得答案
复杂度分析
- 时间复杂度:O(1) 。这题中 n 是一个 32 位数,常数级别,所以运行时间是 O(1) 的。
- 空间复杂度:O(1)。没有使用额外空间。
func hammingWeight(num uint32) int {
//用1去和32位做与(&)运算,如果结果不是0的话 ,说明这个位置的数字就是1
count := 0
mask := uint32(1)
for i:=0; i<32;i++ {
fmt.Println(num & mask)
if (num & mask) != 0{
count++
}
//每次循环后,1的位置就向左移动一位,就像是一个指针扫描一遍这32位数一样
mask = mask << 1
}
return count
}
方法二:上面的方法,速度会稍慢些,因为每次比较都要比较32位,我们可以反向操作,将num向右移1一位,这样省了内存的同时,每次循环,比较的次数都会变少,因为num的位数每次都右移,也就是位数-1
复杂度分析
- 时间复杂度:O(1) 。也是常数级时间复杂度。
- 空间复杂度:O(1)。没有使用额外空间。
func hammingWeight(num uint32) int {
count := 0
//也可以反向操作,将num向右移1一位,这样就省了些内存
/for num !=0 {
if num & 1 == 1{//让num与1进行按位与运算,取得num最低位判断是否位1
count++
}
num >>= 1//num右移一位
}
return count
}
方法三:将 n 和 n−1 做与运算,会把最后一个 1 的位变成 0
n 10100
n-1 10011
n&n-1 10000
复杂度分析
- 时间复杂度:O(1) 。运行时间与 n 中位为 1 的有关。在最坏情况下, n 中所有位都是 1 。对于 32 位整数,运行时间是 O(1) 的。
- 空间复杂度:O(1) 。没有使用额外空间。
func hammingWeight(num uint32) int {
//在二进制表示中,数字 n 中最低位的 1 总是对应n−1 中的 0 。
//因此,将 n 和 n−1 与运算总是能把 n 中最低位的 1 变成 0 ,并保持其他位不变。
sum := 0
for num != 0 {
sum++
num &= (num - 1)
}
return sum
}