50. Pow(x, n)

50. Pow(x, n)

✨核心逻辑

本题采用 快速幂(二进制指数法 / 折半计算) 的策略:

  1. 边界处理与负数幂:如果指数 n 是负数,则需要将底数 x 变为其倒数 1/x,同时将指数变为正数。这里必须将 n 赋值给一个 long 类型变量 N,因为 n = -2147483648Integer.MIN_VALUE)取反时会超出 int 的最大正数范围导致溢出。
  2. 二进制展开:将指数 N 看作二进制数(例如 N = 13,二进制为 1101,代表 8 + 4 + 1)。我们可以通过循环不断将指数 N 右移(N >> 1)来遍历它的二进制位。
  3. 按位乘入:在每次循环中,判断当前指数 N 的最低位 (N & 1) 是否为 1。如果为 1,则将当前的底数 x 乘入最终结果 result 中。
  4. 底数自乘:无论最低位是什么,每次循环都要将底数 x 进行平方处理 x = x * x(即 x^2, x^4, x^8...),以便在下一次循环中的按位判断使用时,能一次性累乘进去。

🔥代码实现(含详细变量注释)

class Solution {
    public double myPow(double x, int n) {
        // N:将传入的 int 型指数 n 转为 long 类型。
        // 必须转成 long,是因为当 n = Integer.MIN_VALUE(-2147483648)时,取反操作 `-n` 会超出 int 的取值范围产生溢出。
        long N = n;
        
        // 如果指数是负数,将底数变为倒数,并将指数转为正数
        if (N < 0) {
            x = 1 / x;
            N = -N;
        }

        // result:用于记录最终计算的幂次结果,初始化为 1.0
        double result = 1.0;
        
        // 快速幂核心循环:只要指数 N 大于 0 就继续运算
        while (N > 0) {
            // 判断 N 的当前二进制最低位是否为 1(即 N 是否为奇数)
            // 如果为 1,则将当前累积的底数 x 乘入 result 中
            if ((N & 1) == 1) {
                result *= x;
            }
            
            // 底数 x 每轮循环都要自乘(平方),形成 x^1, x^2, x^4, x^8... 的序列
            x = x * x;
            
            // 指数 N 向右无符号移动 1 位(相当于除以 2,舍弃最低位)
            N >>= 1;
        }
        
        // 返回最终计算出来的幂运算结果
        return result;
    }
}
  • ⏱️复杂度分析
    • 时间复杂度:O(log N),其中 N 是指数。循环执行的次数等于指数 N 的二进制位数,即为 log_2 N,比暴力循环的 O(N) 算法快得多。

    • 空间复杂度:O(1),仅使用了 N、result 和循环控制变量等常数个额外空间。

🔍总结一下:

这道题无法暴力,2的32次方-1要执行21多亿次,直接超时

可将n转化为二进制,比如10:1010,每位对应的二进制值是8421,也就是x的8次方 × x的2次方,对x不断平方就能得到对应位置的x的多少次方,然后逐次右移N,判断末尾是否为1,是则累乘到结果中即可