计算整数二进制中1的个数

Golang实现:计算整数二进制中1的个数(包含负数补码)

问题分析

这道题目要求我们计算一个整数的二进制表示中1的个数,对于负数需要考虑其补码形式。

例如:

  • 输入:5(二进制:101)→ 输出:2
  • 输入:-3(二进制补码:1111...1101)→ 输出:31(32位系统下)
解题思路

我们可以利用位运算中的与运算(&)来检查整数的每一位是否为1。具体步骤如下:

  1. 初始化计数器为0
  2. 通过循环检查整数的每一位:
    • 将整数与1进行与运算(n & 1),如果结果为1,则计数器加1
    • 将整数右移一位(n >>= 1),继续检查下一位
  3. 对于负数,Go语言中整数采用补码表示,右移时会保留符号位,因此需要特别处理

需要注意的是,对于负数,右移操作会导致最高位一直为1,可能会陷入死循环。因此,我们需要限制循环次数为整数的位数(通常为32位或64位)。

Golang代码实现

下面是Golang语言的实现方案:

package main

import (
    "fmt"
)

// hammingWeight 计算整数二进制中1的个数
func hammingWeight(num int) int {
    count := 0
    // 遍历整数的每一位(32位整数)
    for i := 0; i < 32; i++ {
        // 检查当前位是否为1
        if (num & 1) == 1 {
            count++
        }
        // 右移一位,检查下一位
        num >>= 1
    }
    return count
}

func main() {
    // 测试示例
    fmt.Println(hammingWeight(5))   // 输出: 2 (二进制: 101)
    fmt.Println(hammingWeight(0))   // 输出: 0
    fmt.Println(hammingWeight(-1))  // 输出: 32 (32位系统下的-1的补码是全1)
    fmt.Println(hammingWeight(-3))  // 输出: 31 (32位系统下的-3的补码是1111...1101)
}
    
算法复杂度分析
  • 时间复杂度:O(1),因为我们只需要遍历32位(固定次数)
  • 空间复杂度:O(1),只使用了常数级别的额外空间
代码解析
  1. 函数定义hammingWeight(num int) int 接收一个整数参数,返回其二进制中1的个数

  2. 位运算检查

    • num & 1:检查整数的最低位是否为1
    • num >>= 1:将整数右移一位,准备检查下一位
  3. 负数处理

    • 在Go语言中,整数默认是有符号的,负数采用补码表示
    • 右移操作会保留符号位(算术右移),因此负数的右移不会导致整数变为0
    • 通过固定循环32次(32位整数),确保检查了所有位
优化方案

上述方法需要遍历32位,实际上可以通过更高效的方法减少遍历次数。一个经典的优化是利用n & (n-1)操作:

func hammingWeight(num int) int {
    count := 0
    for num != 0 {
        // n & (n-1) 会将n的二进制表示中最后一个1变为0
        num &= (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语言中采用补码表示的特性。

你可能感兴趣的:(算法,算法,后端)