实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn )。
题解
本题很容易让人想到x^n= x^ n/2 *x^ n/2这个操作,但是如何从底到上以及终止条件是什么呢?这时候就可以用到分治(一种递归),自底向下的分解问题,求解,并向上合并
分治算法由“分”和“治”两部分组成,但是它主要包括 3 个过程:
划分(Divide)
求解(Conquer)
合并(Combine)
其中:
- 划分(Divide):将原问题划分为规模较小的子问题,子问题相互独立,与原问题形式相同。
- 求解(Conquer):递归的求解划分之后的子问题。
- 合并(Combine):这一步非必须。有些问题涉及合并子问题的解,将子问题的解合并成原问题的解。有的问题则不需要,只是求出子问题的解即可。
说白了就是先找找拆分到最小规模问题时怎么解,然后再瞅瞅随着问题规模增大点问题怎么解,最后就是找到递归函数,码出递归代码即可。
根据 n 的取值范围,它可能分为 3 种情况:n 为负数、n 为正数时又分为 n 为奇数、n 为偶数。
所以整个的解题步骤就很明确:
(1) 划分
划分就是拆解到问题的最小规模,这里可以用一点点二分的思想。
n 为偶数时,x^n 可以转换为 x 的 n/2 相乘,然后 x 的 n/2 又可以转换为 x 的 n/4 相乘,…,直到转换成 x^1。
n 为奇数时,先拆出一个 x,n-1 为偶数,再以上面同样的逻辑去分,只是最后记得把拆出来的 x 带上。
n 为负数时,-n 是正数,x 的 n 次方,相当于就是 x^ (-n) ,按照上面求完以后取个倒数。
(2) 求解
递归的求解划分之后的子问题。
最小的情况就是 n = 1 或者 n = 0,此时的值为 x 或者 1。
(3) 合并
一步步的向上合并,合并出最后的解。
图解
以 x = 2.00000,n = 10 为例。
先是自顶向下的分解问题,分解如下图所示:
之后按照自底向上的方式合并解。
class Solution {
//如果是偶数 x^n=x^(n/2)*x^(n/2)
//如果是奇数 x^n=x^(n/2)*x^(n/2)*x
//终止条件 n==1||n==0
//还要特殊考虑x<0的情况
public double myPow(double x, int n) {
return n>0?recur(x,n):1.0/recur(x,n);
}
public double recur(double x, int n) {
if(n==1) return x;
//一定要是1.0,避免返回int类型
if(n==0) return 1.0;
//存值避免后续多次计算
double temp=recur(x,n/2);
if(n%2==0) return temp*temp;
else return temp*temp*x;
}
}
此解法需要计算 x 的 n 次方,x 的 n/2 次方,x 的 n/4 次方,…,x 的 1 次方,所以时间复杂度为 O(logn)。
虽然分治算法没有直接分配额外的数组空间,但是在递归过程中调用了额外的栈空间,分治算法相当于每次将当前问题拆成了两部分,n 在变为 1 之前需要进行 O(logn) 次递归,所以空间复杂度也为 O(logn)。