题目: 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
示例:
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2^-2 = 1/2^2 = 1/4 = 0.25
说明:
一、暴力法,时间超过限制
这个是我们最容易想到的办法,一次for循环就可以解决,但是时间复杂度O(N)超出限制,不能通过题解,代码如下:
double myPow1(double x, int n)
{
if(x==1 || n==0)
return 1;
double sum=1;
int flag=0;
if(n<0)
{
flag=1;
n*=-1;
}
for(int i=0;i<n;i++)
{
sum*=x;
}
if(flag)
{
return 1/sum;
}
return sum;
}
0(N)时间复杂度超出限制,那么正确题解的时间复杂度一定是O(logN),我们想办法往这个时间复杂度上靠近,很容易想到二分,分为2部分进行相乘,那样可以将乘的次数减少一半,这就是基于二分的快速幂。
二、快速幂
假如我们需要求解36,那么可以分解为:(32)3→(93)→(92)*9=81*9=729。每次底数以x2进行递增,指数以num/2变化,分为奇数和偶数情况。那么我们可以总结出一个规律:
因为奇数情况,用除法和偶数的结果一样,如3/2=1,2/2=1,所以不用n-1。我们可以看到这是一种二分的形式,即将n进行二分,求x4=x2*x2,而不是x4=x*x*x*x。如果是奇数的话就会多出来一项,所以我们用res保存结果时,当n为奇数,就将x乘入res,再进行x2,最后n==1时,x已经不断x2累乘计算完毕,此时n为奇数,将x乘入res,得出结果,n/2=0循环结束。
我们可以根据上述的思路进行一个示例演示,假如现在计算55,我们进行过程演示:
n幂值(初始值5) | 奇偶(奇数) | res(1) | x(5) |
---|---|---|---|
n=5 | 奇数 | 1*5=5 | 5^5=25 |
n=5/2=2 | 偶数 | 5 | 25*25=625 |
n=2/2=1 | 奇数 | 5*625=3125 | 625*625=390625 |
n=1/2=0 | 退出循环,返回res=3125 |
计算机验证结果正确,那么我们可以总结思路,需要考虑到负数等情况:
- 如果x=1,或者n=0,那么直接返回1即可。
- 如果n为负数,需要求x的倒数平方,那么x=1/x;n=-n;
如果n用int保存,int有符号整形变量的范围是[-2147483648 2147483647];当-2147483648取绝对值时大于2147483647就会报错出现溢出,所以先对负数进行强制类型转换,用long存储即可。
- 进行while循环,直到n==0时退出。
- 循环体内,进行奇数偶数判断,奇数:res*=x;
- 进行x*=x累加,n/=2不断二分缩小。
- 循环结束返回res即可
(一)常规解法
我们可以根据思路写出代码:
//2.快速幂运算,不引入位运算,3^3=3^2*3^1,所以我们可以不断计算x的平方,分为奇数,偶数两种情况
double myPow2(double x, int n)
{
if(x==1 || n==0)
return 1;
long num=n;//int会溢出,所以用long存储因为有符号整形变量的范围是-2147483648 2147483647;当-2147483648取绝对值时大于2147483647就会报错,所以先对负数进行强制类型转换。
double res=1;
if(n<0)
{
num=-num;
x=1/x;
}
while(num!=0)
{
if(num%2==1)//奇数
{
res*=x;
}
x*=x;
num/=2;
}
return res;
}
(二)借助位运算
我们可以借助位运算来优化代码,降低时间复杂度,我们用到了
num%2==1;//判断奇数偶数
num/=2;//二分
可以用位运算来表示这两句,所有奇数的二进制,0号位置一定是1,所以可以
(num & 1)==1;//加括号表明执行优先级
来判断奇数偶数,如果0号位置为1,返回真,进行res*=x;否则不进入if判断体中。
左移1位表示增大一倍数值,右移1位表示缩小一倍数值,所以,我们可以用>>表示除2运算符:
num>>=1;
那么优化后的代码为:
double myPow(double x, int n)
{
if(x==1 || n==0)
return 1;
long num=n;
double res=1;
if(n<0)
{
num=-num;
x=1/x;
}
while(num!=0)
{
if((num&1)==1)//奇数,与运算可以判断奇数,奇数它一定在0位上有1
{
res*=x;
}
x*=x;
num=num>>1;//右移就是/2的含义
}
return res;
}
加油哦!🎉。