剑指 Offer 16-数值的整数次方(数值的整数次方 LCOF)
LiuYao 2021-10-18
递归<Recursion>
数学<Math>
# 中文题目
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入:x = 2.00000, n = 10 输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3 输出:9.26100
示例 3:
输入:x = 2.00000, n = -2 输出:0.25000 解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104
注意:本题与主站 50 题相同:https://leetcode-cn.com/problems/powx-n/
# 通过代码
class Solution {
public double myPow(double x, int n) {
if(n == 0){
return 1.0;
}
int m = 0;
if(n < 0){
m = -(n/2);
x = 1.0 /x;
}else{
m = n/2;
}
return (n % 2 == 0) ? Math.pow(x*x, m) :x*Math.pow(x*x, m);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public double myPow(double x, int n) {
if(n == 0){
return 1.0;
}
int m = 0;
if(n < 0){
m = -(n/2);
x = 1.0 /x;
}else{
m = n/2;
}
return (n % 2 == 0) ? Math.pow(x*x, m) :x*Math.pow(x*x, m);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public double myPow(double x, int n) {
// 如果x==0,直接返回0,避免后面1/0报错
if (x == 0) {
return 0;
}
// 乘方次数,注意这里要用long型,因为-2147483648的正数+2147483648没办法用int表示,需要使用long
long b = n;
// 结果值
double res = 1.0;
// 如果乘方次数小于0,比如2^-2 可以转换为(1/2)^2
if (b < 0) {
// 底数取倒
x = 1 / x;
// 指数取正
b = -b;
}
// 当指数大于0时
while (b > 0) {
// 如果当前指数为奇数的话,将多出一项x,乘入res
// 最后都会变成 x^n=x^0*res=res
// 当 b为1时,就会将之前的结果都乘入到res中
if ((b & 1) == 1)
res *= x;
x *= x;
b >>= 1;
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 高赞题解
# 解题思路:
求 xn 最简单的方法是通过循环将 n 个 x 乘起来,依次求 x1,x2,...,xn−1,xn ,时间复杂度为 O(n) 。 快速幂法 可将时间复杂度降低至 O(log2n) ,以下从 “二分法” 和 “二进制” 两个角度解析快速幂法。
# 快速幂解析(二进制角度):
利用十进制数字 n 的二进制表示,可对快速幂进行数学化解释。
对于任何十进制正整数 n ,设其二进制为 "bm...b3b2b1"( bi 为二进制某位值,i∈[1,m] ),则有:
- 二进制转十进制: n=1b1+2b2+4b3+...+2m−1bm (即二进制转十进制公式) ;
- 幂的二进制展开: xn=x1b1+2b2+4b3+...+2m−1bm=x1b1x2b2x4b3...x2m−1bm ;
根据以上推导,可把计算 xn 转化为解决以下两个问题:
- 计算 x1,x2,x4,...,x2m−1 的值: 循环赋值操作 x=x2 即可;
- 获取二进制各位 b1,b2,b3,...,bm 的值: 循环执行以下操作即可。
- n&1 (与操作): 判断 n 二进制最右一位是否为 1 ;
- n>>1 (移位操作): n 右移一位(可理解为删除最后一位)。
因此,应用以上操作,可在循环中依次计算 x20b1,x21b2,...,x2m−1bm 的值,并将所有 x2i−1bi 累计相乘即可。
- 当 bi=0 时:x2i−1bi=1 ;
- 当 bi=1 时:x2i−1bi=x2i−1 ;
{:width=450}
# 快速幂解析(二分法角度):
快速幂实际上是二分思想的一种应用。
二分推导: xn=xn/2×xn/2=(x2)n/2 ,令 n/2 为整数,则需要分为奇偶两种情况(设向下取整除法符号为 "//" ):
- 当 n 为偶数: xn=(x2)n//2 ;
- 当 n 为奇数: xn=x(x2)n//2 ,即会多出一项 x ;
幂结果获取:
- 根据二分推导,可通过循环 x=x2 操作,每次把幂从 n 降至 n//2 ,直至将幂降为 0 ;
- 设 res=1 ,则初始状态 xn=xn×res 。在循环二分时,每当 n 为奇数时,将多出的一项 x 乘入 res ,则最终可化至 xn=x0×res=res ,返回 res 即可。
{:width=450}
- 转化为位运算:
- 向下整除 n//2 等价于 右移一位 n>>1 ;
- 取余数 n%2 等价于 判断二进制最右一位值 n&1 ;
# 算法流程:
- 当 x=0 时:直接返回 0 (避免后续 x=1/x 操作报错)。
- 初始化 res=1 ;
- 当 n<0 时:把问题转化至 n≥0 的范围内,即执行 x=1/x ,n=−n ;
- 循环计算:当 n=0 时跳出;
- 当 n&1=1 时:将当前 x 乘入 res (即 res∗=x );
- 执行 x=x2 (即 x∗=x );
- 执行 n 右移一位(即 n>>=1)。
- 返回 res 。
# 复杂度分析:
- 时间复杂度 O(log2n) : 二分的时间复杂度为对数级别。
- 空间复杂度 O(1) : res, b 等变量占用常数大小额外空间。
# 代码:
Java 代码中
int32
变量 n∈[−2147483648,2147483647] ,因此当 n=−2147483648 时执行 n=−n 会因越界而赋值出错。解决方法是先将 n 存入long
变量 b ,后面用 b 操作即可。
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0: return 0
res = 1
if n < 0: x, n = 1 / x, -n
while n:
if n & 1: res *= x
x *= x
n >>= 1
return res
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
class Solution {
public double myPow(double x, int n) {
if(x == 0) return 0;
long b = n;
double res = 1.0;
if(b < 0) {
x = 1 / x;
b = -b;
}
while(b > 0) {
if((b & 1) == 1) res *= x;
x *= x;
b >>= 1;
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 提交历史
提交时间 | 提交结果 | 标记状态 | 我的注释 | 执行时间 | 战胜比例 | 内存消耗 | 语言 |
---|---|---|---|---|---|---|---|
2021-10-18 22:13:10 | Accepted | 🚩🚩 | 注意-n 的整数溢出 | 0 ms | 100.0% | 37.9 MB | java |
2021-10-18 22:11:53 | Wrong Answer | N/A | N/A | N/A | java | ||
2021-10-18 22:11:34 | Wrong Answer | N/A | N/A | N/A | java | ||
2021-10-18 21:49:00 | Runtime Error | N/A | N/A | N/A | java | ||
2021-10-18 21:47:45 | Wrong Answer | N/A | N/A | N/A | java | ||
2021-06-16 23:03:29 | Accepted | 🚩 | 递归的方式计算快速幂 | 1 ms | 100.0% | 37.7 MB | java |
2021-06-16 23:01:11 | Accepted | 🚩 | 通过快速幂,每次乘平方,奇数多乘一个 | 1 ms | 100.0% | 37.8 MB | java |
# 统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
126518 | 372063 | 34.0% |