题:
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
(-100.0 < x < 100.0
n 是 32 位有符号整数,其数值范围是 [−2∧31, 2∧31 − 1] 。)
---------------------------------------------------------------------------------------
一个数的n次幂,一般要分三种情况考虑: n < 0, n = 0, n > 0.
通常解法, 暴力法,对n进行循环,时间复杂度是 O(n)
class Solution {
public double myPow(double x, int n) {
double base = x;
if(x==1){
x=1;
}
else if(n>0){
for(int i=1;i<n;i++){
x=x*base;
}
}else{
for(int i=1;i>n;i--){
x=x/base;
}
}
return x;
}
}
这种解法最容易被想到但是由于复杂度是O(n), 当x,n太大的时侯,这种方法就不太适用了。在运行时间上无法通过提交,因此需要寻找更快的方法。
这里引入一个概念 [快速幂] 。本质是分治算法
原理如下:
将n拆分迭代
------------------------------------------------------------------------------------------
一,使用递归的方式来实现这个算法 时间复杂度为 O(log n)
class Solution {
public double myPow(double x, int n) {
if(x == 1|| n==0){
return 1;}
else{
int temp = n/2;
double res = myPow(x,temp);
if(n>0){
if(n%2==0){
res=res*res;
}
else{
res=res*res*x;
}
return res;
}
else{
if(n%2==0){
res=res*res;
}
else{
res=res*res/x;
}
return res;
}
}
}
}
该算法在leetcode的结果:
执行用时 :
1 ms, 在所有 Java 提交中击败了94.45%的用户
内存消耗 :
37.2 MB, 在所有 Java 提交中击败了5.88%的用户
对于n=11,x=3时候的递归过程详解
输入计算 311
–> 35 * 35 *3
–> ( 32 * 32 3 ) * (32 * 32 3) 3
–> (( 31 31) * ( 31 31) 3 ) * (( 31 31) * ( 31 31) *3) *3
–> (( (303) (30*3)) * ((303) (30*3)) *3 ) * (( (303) (30*3)) * ( (303)(303)) 3) 3
–> (( (3*3) (3*3)) * ((3*3) (3*3)) *3 ) * (((3*3) (3*3)3)) * ((3*3)*3)(3*3)) *3) *3
------------------------------------------------------------------------------------------
二,不使用递归的方式,用于进一步减少资源的消耗
由于递归时需要不断重复计算 比如 33, 35 的值,消耗了很多不必要的资源 (由于递归需要使用额外的栈空间 )。
此处进行优化。 以311举例,
在递归运算中,
311 <-- 先平方再乘3 – 35 <-- 先平方再乘3– 32 <-- 2– 3
上述过程中
- 粗体的原始的3,参与了3次平方计算,所以初始的3最后被乘的次数是23
- 第一次 先平方后乘3 中的3 最后被平方了1次,21
- 最后一个 先平方后乘3 中的3 没有被平方过,所以是 20
最终得到的结果就是
311 = 328 *321 *320 = 3(1011)2
所以我们可以计算对应的二进制位置为1处对应的幂 作为最后结果的乘数。
class Solution {
public double myPow(double x, int n) {
double res = 1;
double contribute = x; // 这个用于每次计算 x, x∧2,x∧3.....
boolean negative = (n<0);
while(n!=0){
if(n%2!=0){// 只有当 n的二进制末位是1 的时候,在res中乘入 contribute
res*=contribute;
}
contribute*=contribute;
n/=2;
}
if(negative){
return 1/res;
}
else{
return res;
}
}
}
不知道为何内存消耗没有变。 XD
内存消耗 :37.3 MB , 在所有 Java 提交中击败了 5.88% 的用户
此外,也可以引入 位运算 来优化循环条件,更好的显示逻辑。