2的幂

📌 定义

给定一个整数 n,判断它是否是 2 的幂次方。即判断是否存在整数 x,使得 n = 2^x。

示例:
输入: n = 1
输出: true
解释: 2^0 = 1

输入: n = 16
输出: true
解释: 2^4 = 16

输入: n = 3
输出: false

输入: n = 5
输出: false

核心思路

2的幂的二进制特征:

  • 2的幂在二进制中只有一个 1
  • 例如:1(1), 2(10), 4(100), 8(1000), 16(10000)

关键技巧: 使用 n & (n - 1) 去掉最右边的 1,如果结果为 0,说明只有一个 1

示例:
n = 8 (1000)
n - 1 = 7 (0111)
n & (n - 1) = 1000 & 0111 = 0000 ✓

n = 6 (110)
n - 1 = 5 (101)
n & (n - 1) = 110 & 101 = 100 ≠ 0 ✗

复杂度分析

方法时间复杂度空间复杂度说明
循环除法O(log n)O(1)不断除以2
位运算O(1)O(1)n & (n-1)
二进制统计O(log n)O(1)统计1的个数
数学方法O(log n)O(1)对数判断

Go 代码

Go 实现

package main
 
import (
    "fmt"
    "math"
)
 
// 方法1: n & (n - 1)
func isPowerOfTwo(n int) bool {
    return n > 0 && (n&(n-1)) == 0
}
 
// 方法2: n & -n
func isPowerOfTwoV2(n int) bool {
    return n > 0 && (n&-n) == n
}
 
// 方法3: 统计1的个数
func isPowerOfTwoCount(n int) bool {
    if n <= 0 {
        return false
    }
 
    count := 0
    for n > 0 {
        count += n & 1
        n >>= 1
        if count > 1 {
            return false
        }
    }
 
    return count == 1
}
 
// 方法4: 数学方法
func isPowerOfTwoMath(n int) bool {
    if n <= 0 {
        return false
    }
 
    logVal := math.Log2(float64(n))
    return logVal == float64(int(logVal))
}
 
// 方法5: 循环除法
func isPowerOfTwoLoop(n int) bool {
    if n <= 0 {
        return false
    }
 
    for n%2 == 0 {
        n /= 2
    }
 
    return n == 1
}
 
func main() {
    testCases := []int{1, 2, 3, 4, 5, 8, 16, 1024}
 
    for _, n := range testCases {
        result := isPowerOfTwo(n)
        fmt.Printf("%d 是2的幂: %v\n", n, result)
    }
}

思路展开

方法1详解:n & (n - 1)

2的幂的特点:
- 二进制中只有一个1
- n & (n - 1) 会去掉最右边的1

示例1: n = 8 (2^3)
n     = 1000
n - 1 = 0111
n & (n - 1) = 1000 & 0111 = 0000 ✓

示例2: n = 6 (非2的幂)
n     = 0110
n - 1 = 0101
n & (n - 1) = 0110 & 0101 = 0100 ≠ 0 ✗

示例3: n = 16 (2^4)
n     = 10000
n - 1 = 01111
n & (n - 1) = 10000 & 01111 = 00000 ✓

方法2详解:n & -n

n & -n 的作用:获取最右边的1

示例1: n = 8 (2^3)
n     = 00001000
-n    = 11111000 (补码)
n & -n = 00001000 = 8 = n ✓

示例2: n = 6 (非2的幂)
n     = 00000110
-n    = 11111010
n & -n = 00000010 = 2 ≠ n ✗

原理:
- 如果n是2的幂,只有一个1
- n & -n 会保留这唯一的1
- 结果应该等于n本身

边界情况处理

特殊情况:
1. n <= 0: 返回 false
   - 0 不是2的幂
   - 负数不是2的幂

2. n = 1: 返回 true
   - 1 = 2^0

3. n = 2^31: 可能溢出
   - 使用long long避免溢出
   - 或者限制n的范围

经典题目

LeetCode 问题

扩展问题

  • 3的幂(不能用位运算)
  • 判断是否为2的幂次方的和

⚖️ 优缺点

优点

  • ✅ 时间复杂度 O(1):只需一次位运算
  • ✅ 空间复杂度 O(1):不需要额外空间
  • ✅ 代码简洁:一行代码解决
  • ✅ 效率高:位运算速度快

缺点

  • ❌ 不够直观:需要理解位运算原理
  • ❌ 仅适用于2的幂:不能推广到其他底数

🎨 应用场景

  • 容量与边界检查:判断缓冲区、哈希表或分块大小是否为 2 的幂,便于使用位掩码取模。
  • 树与堆的层级计算:完全二叉树的层级边界常由 2 的幂决定。
  • 算法前置条件:FFT、循环缓冲区等实现经常要求容量为 2 的幂。

💡 优化技巧

  • 最常用写法是 n > 0 && n&(n-1) == 0,先判断正数避免 0 被误判。
  • 如果只需要判断一个整数,不要循环除以 2;位运算可以在固定机器字长内完成。
  • n & -n == n 也能判断 2 的幂,但依赖补码语义,可读性通常不如 n&(n-1)。

相关主题


返回:位运算 | 算法学习导航