50.Pow(x,n)
1.题目介绍
2.题解
本题的方法被称为「快速幂算法」,有递归和迭代两个版本。这篇题解会从递归版本的开始讲起,再逐步引出迭代的版本。
当指数 n为负数时,我们可以计算 x^-n再取倒数得到结果,因此我们只需要考虑 n 为自然数的情况。
2.1 模拟(不推荐,时间复杂度过高)
思路
分为n>=0, n<0情况分别计算Pow(x,n)
代码
class Solution {
public:
double myPow(double x, int n) {
double ans = 1;
int num = n >=0 ? n:-n;
for(int i = 0; i < num; i++){
ans *= x;
}
if(n < 0) ans = 1.0 / ans;
return ans;
}
};
2.2 快速幂 + 递归
思路
由于每次递归都会使得指数减少一半,因此递归的层数为 O(logn),算法可以在很快的时间内得到结果。
代码
class Solution {
public:
double quickMul(double x, long long N) {
if(N == 0) return 1.0;
double y = quickMul(x, N / 2.0);
return N % 2 == 0? y*y : y*y*x;
}
double myPow(double x, int n) {
long long N = n; // 这里为何要强转为long long? 因为后面-n进行取反时,-2147483648(INT_MIN,整型最小值)进行取反操作,INT_MIN 的取反结果会导致溢出,产生未定义的行为。
return N >= 0 ? quickMul(x, N): 1.0 / quickMul(x, -N);
}
};
2.3 快速幂 + 迭代
思路
由于快速幂的核心理念是每次将上次的结果平方,相当于x(2n) ,但由于在中间乘上额外的x,我们不知道何时触发导致迭代写起来比较麻烦
但是我们仔细观察发现,将初始的x和后面额外乘上的x分开平方
\(x^n=x^{2^{i_0}}\times x^{2^{i_1}}\times\cdots\times x^{2^{i_k}}\)
最后化简可以得到
\(n=2^{i_0}+2^{i_1}+\cdots+2^{i_k}\)
其实就是n的二进制表达式,我们只要n在二进制位为1的值位置
就知道何时要额外乘一个x了
比如像n = 10(1010)是,其实就是2^2 * 2^8
所以我们可以维护一个值专门记录 2^n 的值 x_contributor
当n % 2 == 1时,我们就将结果记录值ans *= x_contributor即可
代码
class Solution {
public:
double quickMul(double x, long long N) {
double ans = 1.0;
double x_contributor = x;
while(N > 0){
if(N % 2 == 1) ans *= x_contributor;
x_contributor *= x_contributor;
N /= 2.0;
}
return ans;
}
double myPow(double x, int n) {
long long N = n;
return N >= 0? quickMul(x,N):1.0 / quickMul(x,-N);
}
};