计算整数二进制中1的个数
Golang实现:计算整数二进制中1的个数(包含负数补码)
问题分析
这道题目要求我们计算一个整数的二进制表示中1的个数,对于负数需要考虑其补码形式。
例如:
- 输入:
5
(二进制:101
)→ 输出:2
- 输入:
-3
(二进制补码:1111...1101
)→ 输出:31
(32位系统下)
解题思路
我们可以利用位运算中的与运算(&)来检查整数的每一位是否为1。具体步骤如下:
- 初始化计数器为0
- 通过循环检查整数的每一位:
- 将整数与1进行与运算(
n & 1
),如果结果为1,则计数器加1 - 将整数右移一位(
n >>= 1
),继续检查下一位
- 将整数与1进行与运算(
- 对于负数,Go语言中整数采用补码表示,右移时会保留符号位,因此需要特别处理
需要注意的是,对于负数,右移操作会导致最高位一直为1,可能会陷入死循环。因此,我们需要限制循环次数为整数的位数(通常为32位或64位)。
Golang代码实现
下面是Golang语言的实现方案:
package mainimport ("fmt"
)// hammingWeight 计算整数二进制中1的个数
func hammingWeight(num int) int {count := 0// 遍历整数的每一位(32位整数)for i := 0; i < 32; i++ {// 检查当前位是否为1if (num & 1) == 1 {count++}// 右移一位,检查下一位num >>= 1}return count
}func main() {// 测试示例fmt.Println(hammingWeight(5)) // 输出: 2 (二进制: 101)fmt.Println(hammingWeight(0)) // 输出: 0fmt.Println(hammingWeight(-1)) // 输出: 32 (32位系统下的-1的补码是全1)fmt.Println(hammingWeight(-3)) // 输出: 31 (32位系统下的-3的补码是1111...1101)
}
算法复杂度分析
- 时间复杂度:O(1),因为我们只需要遍历32位(固定次数)
- 空间复杂度:O(1),只使用了常数级别的额外空间
代码解析
-
函数定义:
hammingWeight(num int) int
接收一个整数参数,返回其二进制中1的个数 -
位运算检查:
num & 1
:检查整数的最低位是否为1num >>= 1
:将整数右移一位,准备检查下一位
-
负数处理:
- 在Go语言中,整数默认是有符号的,负数采用补码表示
- 右移操作会保留符号位(算术右移),因此负数的右移不会导致整数变为0
- 通过固定循环32次(32位整数),确保检查了所有位
优化方案
上述方法需要遍历32位,实际上可以通过更高效的方法减少遍历次数。一个经典的优化是利用n & (n-1)
操作:
func hammingWeight(num int) int {count := 0for num != 0 {// n & (n-1) 会将n的二进制表示中最后一个1变为0num &= (num - 1)count++}return count
}
这个优化的核心思想是:n & (n-1)
操作会清除n的二进制表示中最后一个1。例如:
n = 1010
(十进制10),n-1 = 1001
,则n & (n-1) = 1000
(清除了最后一个1)- 每执行一次这个操作,就会清除一个1,直到n变为0
这样,对于一个有k个1的整数,只需要循环k次,时间复杂度为O(k),比固定循环32次要高效。
总结
这个算法题主要考察位运算的应用和对整数二进制表示的理解。通过与运算(&)和右移操作(>>),我们可以高效地计算整数二进制中1的个数。对于负数,需要注意Go语言中采用补码表示的特性。