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] 。
方法一: 递归
-
递归关系:当exponent为偶数时, Power(base, exponent) = Power(base*base, exponent/2)
当exponent为奇数时, Power(base, exponent) = base * Power(base*base, exponent/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;
}
方法二: 快速幂
方法一是自上而下的递归的方法, 也可以采用自下而上的循环迭代的方法。
-
先考虑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实际上是一个同步此消彼长的过程。
-
如果exponent是奇数, 比如说求 3^15 次方, 迭代过程如下
base exponent 写为
3 15 3 * 3 ^ 14 = 3 * 9 ^ 7
9 7 9 * 81 ^ 3每次遇到奇数的迭代过程, 都会留下一个base值没有参与到下一次的迭代, 所以每逢遇到奇数, 都要把当前的这个base值计算进结果里。
-
要写一个完整的程序, 还需要考虑一下几个小细节。
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;
}