Pow(x,n)
编号:0010
试题来源:leetcode
题目描述
实现 p o w ( x , n ) pow(x,n) pow(x,n),即计算 x x x的 n n n次幂
其中 − 100.0 < x < 100.0 -100.0 < x<100.0 −100.0<x<100.0
n n n是32位有符号整数,其数值范围是 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [−231,231−1],也就是
c++
中int
的取值范围
示例
-
示例1:
-
示例2:
- 示例3:
解答算法
递归(我的解法)
算法思路
对于底数 x x x来说,有特殊情况 x = = 1 x==1 x==1,此时不用判断 n n n直接输出 0 0 0就好。
对于指数 n n n来说,当 n = = 0 n==0 n==0的时候,不适用于递归表达式,直接输出 1 1 1就好
对于一般情况来说, x n x^n xn有两种可能,一种是 n n n是奇数,另一种是 n n n是偶数,当 n n n是偶数的时候,显然 x n = x n 2 ∗ x n 2 x^{n} = x^{\frac{n}{2}}*x^{\frac{n}{2}} xn=x2n∗x2n这里面,显然把 x n 2 x^{\frac{n}{2}} x2n利用了两次,这样的话,就相对于逐个递乘减少了一般的工作量;当 n n n时偶数的时候, x n = x n 2 ∗ x n 2 ∗ x x^{n} = x^{\frac{n}{2}}*x^{\frac{n}{2}} * x xn=x2n∗x2n∗x只是在之前的基础上多乘了一个x,计算步骤也是很小的。
然后递归的终止条件就是 n = = 1 n==1 n==1,此时,只要返回 x x x就好。
当计算
n
<
0
n<0
n<0的时候,我用的是
x
n
=
1
x
−
n
x^{n} = \frac{1}{x^{-n}}
xn=x−n1,但是这里存在越界的可能,对于
−
2
31
-2^{31}
−231来说,其相反数
2
31
2^{31}
231不能用int
类型表示,因此我把式子改写成了
x
n
=
1
x
−
n
−
1
∗
x
x^n=\frac{1}{x^{-n-1}*x}
xn=x−n−1∗x1这样就避免了
−
n
-n
−n越界的可能性。
实现代码
class Solution {
public:
double myPow(double x, int n) {
if (x == 1) //当底数为1的时候,直接输出1
return 1;
if (n < 0) //当指数小于0的时候,先转换成指数大于0的情况
return 1 / (x * myPow(x, -(n + 1)));
if (n == 0) //当指数为0的时候,直接输出1
return 1;
if (n == 1) //当指数为1的时候,递归终止
return x;
double result = myPow(x, n / 2); //递归调用
if (n % 2 == 0) //偶数和结束分开讨论
return result * result;
if (n % 2 == 1)
return result * result * x;
return 0; //这个是因为程序要求一定要有返回值,所以加上的,无意义
}
};
复杂度分析
- 时间复杂度:每一次递归仅进行了一次操作,一共进行了 l o g n logn logn次递归,因此时间复杂度 O ( l o g n ) O(logn) O(logn)
- 空间复杂度:整个过程中用到的空间是递归用到的栈空间,深度应当是 l o g n logn logn,因此空间复杂度 O ( l o g n ) O(logn) O(logn)
这是我自己第一次两个100%击败,有点意思—__—
递归(官方解答)
算法思路和复杂度分析和我的一样,不列举了。
代码实现
class Solution {
public:
double quickMul(double x, long long N) { //指数变成N,避免了溢出问题
if (N == 0) { //指数为0的情况
return 1.0;
}
double y = quickMul(x, N / 2); //递归调用
return N % 2 == 0 ? y * y : y * y * x; //这个对奇偶的利用有点简洁
}
double myPow(double x, int n) {
long long N = n;
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N); //对正负的利用
}
};
三目运算符真好使
迭代(官方解答)
算法思路
迭代思路用到了整数的二进制表示, n n n是一个十进制整数,它一定可以被转化成一个二进制数 i k i k − 1 … i 0 i_ki_{k-1}\dotso i_0 ikik−1…i0,那么 n = 2 i 0 + 2 i 1 + … + 2 i k n=2^{i_0}+2^{i_1}+\dotso + 2^{i_k} n=2i0+2i1+…+2ik,因此 x n = x 2 i 0 ∗ … ∗ x 2 i k x^n=x^{2^{i_0}}*\dotso *x^{2^{i_k}} xn=x2i0∗…∗x2ik,因此只要知道了二进制数,可以求解其对应的 x n x^n xn
代码实现
class Solution {
public:
double quickMul(double x, long long N) {
double ans = 1.0;
// 贡献的初始值为 x
double x_contribute = x;
// 在对 N 进行二进制拆分的同时计算答案
while (N > 0) {
if (N % 2 == 1) {
// 如果 N 二进制表示的最低位为 1,那么需要计入贡献
ans *= x_contribute;
}
// 将贡献不断地平方
x_contribute *= x_contribute;
// 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
N /= 2;
}
return ans;
}
double myPow(double x, int n) {
long long N = n;
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
}
};
复杂度分析
- 时间复杂度:整个过程中进行了 l o g n logn logn层循环,因此时间复杂度为 O ( l o g n ) O(logn) O(logn)
- 空间复杂度:显然 O ( 1 ) O(1) O(1)