1. 题目
实现 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. 解题思路
递归问题三步骤:子问题、变量、边界条件
对于求解pow问题的子问题就是n次方等于x*n-1次方,即pow(x, n) = x*pow(x, n-1)。
本题的变量就是次数n。
注意到n可以为负数,所以边界条件有三个,分别是n = 0,1,-1。
通过这样可以编写出简单的递归代码,如下:
class Solution {
public:
double myPow(double x, int n) {
if (n == 0) return 1;
if (n == 1) return x;
if (n == -1) return 1/x;
if (n > 0) return x*myPow(x, n-1);
else return 1/x * myPow(x, n+1);
}
};
但是在leetcode上提交会发现stack-overflow堆栈溢出错误,这是由于对于较大的n的时候递归调用了太多的函数,而堆栈内存有限的。
那么怎么解决呢?可以采用分治的思想去减少递归调用次数。
分治的思想就是把一个大问题划分为多个小问题求解,小问题如果依然不能解决那么再划分,直到小问题可以直接解决为止。
这题利用分治的思想可以如下思考:
我们想求解x的n次方,那么如果n是偶数,x的n次方是不是就等于x的n/2次方的平方呢?只需要求解出x的n/2次方就能求解得到x的n次方了,这里求解x的n次方就是大问题,求解x的n/2次方就是小问题。同理,在求解x的n/2次方时如果不能直接得到答案,可以继续求解x的n/4, n/8…等子问题。
那么什么样的小问题能直接得到答案呢?这个就是递归的边界条件了,当n = 0,1,-1时能直接得到其答案。
跟据分治思想,编写代码如下:
class Solution {
public:
double myPow(double x, int n) {
double ans;
if (n == 0) return 1;
if (n == 1) return x;
if (n == -1) return (1/x);
if (n>0){
if (n%2==0){
ans = myPow(x, n/2)*myPow(x, n/2);
}
else{
ans = x*myPow(x, n/2)*myPow(x, n/2);
}
}
else if (n <0){
if (n%2==0){
ans = myPow(x, n/2)*myPow(x, n/2);
}
else{
ans = (1/x)*myPow(x, n/2)*myPow(x, n/2);
}
}
return ans;
}
};
我天真的以为这样就结束了,但是提交后发现,超时…
经过冷静的思考后发现,上面的分治函数有大量的重复计算,比如,我在计算
x
4
{x^4}
x4时,计算了两遍
x
2
{x^2}
x2,而在计算
x
8
{x^8}
x8时,里面每一次计
x
4
{x^4}
x4都是从头开始计算…于是超时了…
那么怎么避免重复计算呢?我想到了用map去构造一个计算表,每次计算前先判断是否已经计算过,如果计算过就直接调用,否则调用自己递归计算。
最终代码如下:
class Solution {
public:
map<int, double> table; // 存储结果map表
double myPow(double x, int n) {
double ans;
if (n == 0) return 1;
if (n == 1) return x;
if (n == -1) return 1/x;
if (table.find(n/2) == table.end()){ //当在表中找到结果时才计算新值
table[n/2] = myPow(x, n/2);
}
if (n < 0){
if (n %2 == 0){
ans = table[n/2]*table[n/2];
}
else if (n%2 != 0){
ans = (1/x)*table[n/2]*table[n/2]; // n小于0时和大于0主要区别就是这的(1/x)
}
}
else{
if (n %2 == 0){
ans = table[n/2]*table[n/2];
}
else if (n%2 != 0){
ans = (x)*table[n/2]*table[n/2];
}
}
return ans;
}
};
这次,终于通过啦~~