Medium | LeetCode 50 | 剑指 Offer 16. 数值的整数次方 | 递归 | 快速幂

剑指 Offer 16. 数值的整数次方

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入: 2.00000, 10
输出: 1024.00000

示例 2:

输入: 2.10000, 3
输出: 9.26100

示例 3:

输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

说明:

  • -100.0 < x < 100.0
  • n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。

方法一: 递归

  1. 递归关系:当exponent为偶数时, Power(base, exponent) = Power(base*base, exponent/2)
    当exponent为奇数时, Power(base, exponent) = base * Power(base*base, exponent/2)

  2. 递归出口: 当exponent = 1时, 返回base;

/**
 * 方法一 : 采用递归的方式求幂
 */
public double myPowerUnsign1(double x, long n) {
    if (n == 0) {
        return 1;
    }
    if (n == 1) {
        return x;
    }
    double res = myPowerUnsign(x, n >> 1);
    if ((n & 0x1) == 1) {
        return x * res * res;
    }
    return res * res;
}

方法二: 快速幂

方法一是自上而下的递归的方法, 也可以采用自下而上的循环迭代的方法。

  1. 先考虑exponent为偶数的情况, 比如求 2 ^ 16, 此时base = 2, exponent = 16, 每次迭代 base = base * base, exponent /= 2, 迭代过程如下:

    base exponent
    2 16
    4 8
    16 4
    256 2
    256 * 156 1

可以看到 base和exponent实际上是一个同步此消彼长的过程。

  1. 如果exponent是奇数, 比如说求 3^15 次方, 迭代过程如下

    base exponent 写为
    3 15 3 * 3 ^ 14 = 3 * 9 ^ 7
    9 7 9 * 81 ^ 3

    每次遇到奇数的迭代过程, 都会留下一个base值没有参与到下一次的迭代, 所以每逢遇到奇数, 都要把当前的这个base值计算进结果里。

  2. 要写一个完整的程序, 还需要考虑一下几个小细节。

    3.1 幂值是可以为负数的

    3.2 0的负数幂是错误的, 应该让用户知道, 可以选择抛异常, 设置返回状态码等方式告诉用户

public double myPow(double x, int n) {
    // 错误值
    if (x == 0 && n < 0) {
        return 0.0;
    }
    double res = myPowerUnsign(x, Math.abs((long)n));
    if (n < 0) {
        return 1/res;
    }
    return res;
}

/**
 * 方法二 : 采用迭代的方式求幂
 */
public double myPowerUnsign2(double x, long n) {
    if (n == 0) {
        return 1;
    }
    if (n == 1) {
        return x;
    }
    double res = 1.0;
    while (n > 0) {
        if ((n & 0x1) == 1) {
            // 当奇数时, 会将底数乘进结果集里
            // 最终一定会回到n = 1的情况, 这种情况也是将最终迭代的结果计算进res的情况, 
            // 其他的情况只不过是x和n此消彼长的过程, 并不将迭代的结果计算进res里
            res *= x;
        }
        // x 和 n 此消彼长
        x *= x;
        n >>= 1;
    }
    return res;
}