剑指Offer16 数值的整数次方
题目描述
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,x^n)。不得使用库函数,同时不需要考虑大数问题。
分析
暴力法
由于题目要求不能使用库函数,根据简单的数学知识,我们有如下思路
- 当 n=0 ,直接返回 1
- 当 n>0 ,使用循环将x累乘n次
- 当 n<0 ,把问题转化至 n>0的思路,即执行
x=1/x, n=-n
如下代码1所示,这是最通俗易懂的思路,但是其时间复杂度很高O(n),在n较大的情况下会超时
快速幂
推导
对于任何十进制整数 n 来说,其都可以用二进制表示, bmbm-1bm-2……b1 (bi为二进制第i位)
以13为例,其二进制转换为,13=(1101)b
二进制转十进制:
那么:
以x13为例,
思路
根据以上推导,可以把计算xn转换为解决以下两个问题
- 依次计算x的次方的值:循环赋值x=x*x即可。
- 依次获取n的二进制位
- n&1:判断n的二进制最后一位是否为1
- n>>1:删除最后一个二进制位
- 如果二进制为0,即bi=0,那么
,如果二进制为1,即bi=1,那么
由于Java 中 int 变量 n∈[−2147483648,2147483647],如果当n=−2147483648时执行n=-n,就会导致越界,因此用一个long变量temp保存n值,后期一直使用temp计算即可。
如代码2所示
代码
代码1
public double myPow1(double x, int n)
{
if (x == 0)
{
return 1;
}
double res = 1;
if (n < 0)
{
x = 1 / x;
n = -n;
}
while (n > 0)
{
res *= x;
n--;
}
return res;
}
代码2
public double myPow(double x, int n)
{
if (x == 0)
{
return 1;
}
double res = 1.0;
//指数可能过大
long temp = n;
//如果指数为负数,那么底数变为倒数,指数变为相反数
if (temp < 0)
{
x = 1 / x;
temp = -temp;
}
while (temp > 0)
{
//从二进制最低位开始
//该位是否为1,如果为1,累乘,如果为0,相当于乘1,就不计算
if ((temp & 1) == 1)
{
res *= x;
}
//下一位乘的x为上一位的平方
x *= x;
//二进制为右移
temp >>= 1;
}
return res;
}