今天要和大家分享的是「力扣」第 50 题:Pow(x, n)。这题有一个名称叫「快速幂」,我们这里只分享「递归」和「非递归」的写法,其中 「递归」对应「当指数为奇数时,把指数分解成偶数 + 1,当指数为偶数时,把指数除以 2」,「非递归」对应把指数转化成二进制。「快速幂」还有矩阵的求法,感兴趣的朋友可以在网络上自行搜索(我也不会)。
下面说正事。
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-
方法一:递归(分治算法)
这个思路关键的地方在于:不断降低指数。
例如:,为什么要把 单独拿出来呢,这是因为 指数为偶数的时可以让底数变大,而指数变成原来的一半。。把递归树画出来就是下面这个样子,到指数为 或者为 的时候递归终止。
拆分的规律如下:
- 如果指数为奇数,拆成偶数 + 1;
- 如果指数为偶数,拆成指数除以 2;
下面画一个递归树理解拆分的过程。
程序在计算的时候,先不停地让指数降低,然后再一层一层向上传递结果的方式计算出 ,大家可以自行想象程序是如何计算结果的。
参考代码 1:
public class Solution {
public double myPow(double x, int n) {
long N = n;
if (N < 0) {
return 1 / dfs(x, -N);
}
return dfs(x, N);
}
private double dfs(double x, long n) {
if (n == 0) {
return 1;
}
// n % 2 == 1 包含了 n = 1 这种情况
if (n % 2 == 1) {
return x * dfs(x, n - 1);
}
return dfs(x * x, n / 2);
}
}
说明:
-
当指数
n为负数的时候,; -
由于
n有可能等于 ,因此一开始的时候需要先把n转换成长整型; -
递归函数如果实在没有什么好的名字,叫
dfs是比较通用的,这里其实叫pow也可以,只是为了和主调用函数区分(虽然 Java 有重载),所以叫另外一个名字。
时间复杂度:,。
如果把递归解法看成「自顶向下」解决问题,其实这个问题还可以「自底向上」地解决。
想法是:从 开始,依次计算 、、、 ……,让指数成倍增加。在自增的过程中,如果 需要其中的数,把它乘到结果集中。因此拆分的规律是:把指数拆成 的方幂的和。例如:把 看成 。 这个过程其实就是把一个十进制整数转化为二进制的过程。
方法二:把指数转换成二进制
例如计算 ,其中 可以看成:
于是 , 的方幂前的系数是我们需要知道的,把十进制转换为二进制我们都很熟悉了,不断除以 2,看余数。
下面的「参考代码 2」就是上面的运算的模拟。
参考代码 2:
public class Solution {
public double myPow(double x, int n) {
long N = n;
if (n < 0) {
x = 1 / x;
N = -N;
}
double res = 1.0;
while (N > 0) {
if (N % 2 == 1) {
res *= x;
}
x *= x;
N /= 2;
}
return res;
}
}
时间复杂度:,。
这就是今天要和大家分享的内容,感谢大家的收看。