思路
如果使用暴力法很容易超时,可以使用快速幂。
快速幂就相当于二分法一样。我们可以将 2100,可以平均分为 250 x 250 。这样一来,我们就只需要计算一个 ret = 250,之后将得到的结果自乘 ret * ret,就可以得到 2100。
这个看似简单,但是可以节省大量的重复计算。
这里主要使用两种方式,一种是递归,一种是迭代的方法。两种解法都需要注意的问题:
- 给定的 n值可能是负数,即 2-2是等于 1/4的
- 将 n值从负数转换成正数的时候,可能发生整型溢出 - Integer.MIN_VALUE
- 当 n=0的时候,当 x=1的时候或 x=0的时候,预先进行处理可以少进行很多计算。
递归
class Solution {
public double myPow(double x, int n) {
// 如果 n<0,那么 x不能当被除数
if(x == 0) return 0;
// 任何数字的 0次方都等于 1
if(n == 0) return 1;
// 当 n = Integer.MIN_VALUE的时候,取反会发生 Integer数据溢出,所以用另一个 long类型接收它
long b = n;
if(b < 0){
b *= -1;
x = 1/x;
}
return helper(x, b);
}
public double helper(double x, long pow){
// 递归的返回条件,递归一般都是需要返回条件的,要不然就会无限递归下去。。。
if(pow == 1) return x;
// 计算当前 x的 pow/2 次方,之后将返回的结果自乘就可以得到 x的 pow次方
double ret = helper(x, pow/2);
ret *= ret;
// 不过这里需要注意,如果 pow是奇数,那么结果 ret中就少乘了一次 x,补上
if(pow % 2 == 1) return ret*x;
return ret;
}
}
迭代
这个解法是参考 Krahets’s Blog的,写得非常妙。
- 判断是不是奇数:使用
if((b & 1) == 1) ...;
,任何奇数的最后一位都会是 1 b >>= 1;
等价于b /= 2;
class Solution {
public double myPow(double x, int n) {
// 这里只需要处理 x=0的情况
if(x == 0) return 0;
long b = n;
double res = 1.0;
if(b < 0) {
x = 1 / x;
b = -b;
}
while(b > 0) {
// 如果此次是奇数,那么 x*=x就会少乘以一个 x,这里补上
// 当最后,终有一天 b会等于 1的,这样 res就可以附上 x的的值了
if((b & 1) == 1) res *= x;
x *= x;
// 相当于 b/2
b >>= 1;
}
return res;
}
}