题目:
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
递归解法
这里采用的思想是分治思想,比如y = xxxxx…x,则如果我们将分为两半,则我们只要计算一半的值,也就是y = x^(n/2),则结果就是yy。通过减少计算的回合数来减少时间复杂度。
这里要思考的是如果n为偶数情况和n为奇数的情况,我们来分析一下
n为偶数: 则我们直接按y = x(n/2)去计算即可,比如24,则通过计算y = 2^2,从而返回结果为yy = 4即可。
n为奇数:这里我们就要拿出一个x出来,此时剩余的个数就为偶数,则继续按偶数去计算,比如25,则我们一样计算y=22,只要再结果的时候,多乘x上去返回即可。即返回结果为yyx = 44*2 = 32。
这里分治是用递归实现,回到我们的递归思想:
1 递归出口 : 当n=0的时候,返回1
2 递归函数: 入参x和n,计算输入的x和n的幂次方
3 返回值:返回2对应的幂次方
这里的重点是递归函数如何实现,首先,分治,那么我们肯定输入的n一定是2/n,则我们要实现的效果是怎样呢,也就是返回的结果y,我们要的y是x的二分之幂次方的结果。
代码如下:
/**
* @param x
* @param n
* @return
*/
public static double myPow(double x, int n) {
// 将n转为长整型,否则当n为负数,转为正数会溢出,所以这里直接另外新建一个函数来完成
long n1 = n;
return divide(x,n1);
}
/**
* 分治算法 :
* 1. 递归出口,如果是n=0则返回1
* 2. 如果n小于0的情况,则要将x进行倒数,并且n变为正数
* 3. 将n分为2部分,则
* 当n为偶数时,我们只要计算其中一部分的值为y,则返回的结果为y*y即可
* 当n为奇数时,则我们提取出一个x出来,此时又变为偶数,则返回结果为x*y*y即可。
* @param x
* @param n
* @return
*/
private static double divide(double x, long n) {
// 如果n小于0的情况,则要将x进行倒数,并且n变为正数
if (n < 0) {
n = -n;
x = 1/x;
}
// 1. 递归出口,如果是n=0则返回1
if (n == 0) {
return 1.0;
}
// 2. 递归进行获取结果
double y = divide(x, n/2);
// 根据n的奇偶数返回结果集
if (n % 2 == 0) {
return y * y;
}else {
return x * y * y;
}
}
非递归方式
我们要思考一下,那么递归就对应与非递归的while语句,那么停止循环的条件是什么呢,依然是n,则依然是判断n是否等于0结束循环。
接下来是while循环内部是怎样的呢?
回顾下我们递归是怎样实现分治的:
当为奇数,则直接相乘;当为偶数,则要再乘一个x;
那么我们再while循环里:
1 判断是否是奇数,是的话,则要多乘x,这里结果为yx
2 将x进行叠乘,让x = xx (乘到n/2)
3 为了让n一直变到n/2,并且是分治,我们每次让n都做除2操作,也就是对应与右移操作。
/**
* 非递归方式
* @param x
* @param n
* @return
*/
public static double myPow1(double x, int n) {
// 将n转为长整型,否则当n为负数,转为正数会溢出,所以这里直接另外新建一个函数来完成
long n1 = n;
double y = 1;
// 1. 如果n小于0的情况,则要将x进行倒数,并且n变为正数
if (n1 < 0) {
// 如果为负数,则转为正,这里不能是n1 = -n,这样如果负数是最小值,转为正会溢出
n1 = -n1;
x = 1/x;
}
while (n1 != 0) {
// 非递归方式的话,每次怎样判断n的幂次,
if ((n1 & 1) != 0 ) {
// 奇数情况,比如5对应二进制为0101,则0101 & 0001 = 0001,所以不等于0,为奇数
y = y * x;
}
// x进行叠乘
x = x*x;
// 这里相当于将n1/2,右移操作
n1 = n1>>1;
}
return y;
}