LeetCode_50.Pow(x, n)
难度:中等
目录
作者的原始思路
暴力求解
class Solution {
public double myPow(double x, int n) {
double res = 1;
if (x == 1 || x == 0) {
return x;
}else if (x == -1) {
return n%2 == 0? 1 : -1;
}
if (n < -100) {
return 0;
}
if(n < 0) {
while (n != 0) {
res *= (1/x);
n++;
}
return res;
}else if (n == 0) {
return 1;
}else {
while (n != 0) {
res *= x;
n--;
}
}
return res;
}
}
代码简述:
- 先处理特殊情况,例如当x = 1或x = 0的情况,直接返回即可,没必要去算,当x = -1的时候我们只需要判断幂的奇偶性就可以判断出来答案,再考虑一个特殊情况,当n足够小的时候直接返回0即可
问题
- 代码的效率太低,代码的时间复杂度是O(N)线性阶
- 能否一个比线性阶还要低的时间复杂度,明显的是,我们可以用O(logN)对数阶来实现
官方解法-快速幂算法
题目分析
- 题目要求我们计算X^n,即翻译过来就是"X1*X2*X3....*Xn"有n个X进行相乘,最后的结果就是题目想要的结果
- 通过这里,我们仔细研究可以发现,这里有大量的重复的操作,即X*X*X....,我们需要对这些重复的操作做出对应的处理
- 如果我们可以通过快速幂的算法来减少计算次数,从而优化算法
思路分析
- 我们可以采用二分的思想(快速幂的前提)去解决该问题,即2^10 = 2^5 * 2^5,将该运算用二分的思想分开两半进行运算
- 不断重复以上过程,直到分完为止
- 显然这样的算法没有一个一个去算,而采用折半去算,大大减少了运算次数
代码实现
class Solution {
public double myPow(double x, int n) {
if (x == 1 || n == 0) {
return 1;
}
var res = 0.0;
if (n < 0) {
res = 1/calculate1(x,-n);
}else {
res = calculate1(x,n);
}
return res;
}
public double quickMul(double x, long N) {
if (N == 0) {
return 1.0;
}
double y = quickMul(x, N / 2);
return N % 2 == 0 ? y * y : y * y * x;
}
}
代码如何根据算法思想转化而来的?
- 1.根据算法思想,我们可以得出以下的表达式
- 2.假设我们建立一个专门完成这件事的方法,其参数列表与返回值类型如下
- 3.先看左半部分,作为调用者,我们希望最开始传递2与10进去
- 如果我们传递(2,10),则就是1中的第一行与2^10对应
- 这说明了下一次递归要传递(2,5) -> (2,2) -> (2,1)这些参数
- 我们得出了这样一个递归代码
- 4.再看右半部分
- 我们发现,想要获得2^10的结果,我们需要两个2^5相乘
- 进一步观察,我们发现,左边的次幂是奇数的时候,需要多一个2,即多一个x,当左边的次幂是偶数的时候,就不需要,我们可以得出以下代码
- 5,最后再看整体部分
- 我们发现n=1的时候,就算完了,所以而且值就等于2,所以我们得出以下代码
- 6,代码汇总后,得到如下代码
- 7,代码的优化
- 我们发现上面的代码即return calculate(x,n/2) * calculate(x,n/2);代码冗余了
- 比如此刻n=10,x=2,两个方法传过去的都是5,结果一定是一样的,即代码冗余了
- 代码冗余产生的后果是递归多了一半,效率大大降低,此刻我们需要做出以下的更改
即我们只递归一次,将返回值类型用y接受
- 再利用三目操作符完成以上的两个if
- 这有效的减少了一半的递归
结论
快速幂的思想还是挺简单的,即用二分的思想就可以把算法的思想以及递推公式求解出来,但是,快速幂也不简单,不简单在怎么用递推公式推出算法思想,最后,总结一下最重要的几点
1.递推公式的创建
2.如何用递推公式得出代码
3.快速幂的使用场景