1. 问题引入
求 最朴素的方法是把 连乘 次,时间复杂度为 。
当 很大时,这种线性时间复杂度的算法效率较低,容易导致超时。快速幂法可将时间复杂度降低至 。
我们可以从 “迭代(二进制角度)” 和 “递归(分治法角度)” 两个角度来实现快速幂算法。
2. 方法一:迭代快速幂(二进制角度)
2.1 算法原理
利用整数 的二进制表示,可对快速幂进行数学化解释。
对于任何十进制正整数 ,设其二进制表示为 (其中 ,),则有:
- 二进制转十进制:
- 幂的二进制展开:
根据以上推导,可把计算 转化为解决以下两个问题:
- 计算 的值: 循环赋值操作 即可。
- 获取二进制各位 的值: 循环执行以下操作:
n & 1(与操作): 判断 二进制最右一位是否为 1。n >> 1(移位操作): 右移一位(相当于删除最后一位)。
因此,在循环中依次处理 ,并把所有二进制位为 的项累计相乘即可。

下图直观地展示了迭代计算中 的平方以及 二进制位的变化过程:

2.2 迭代过程示例:以 为例
以上面的二进制拆分为基础,我们可以进一步观察快速幂循环中各个变量的变化过程。
对于 :
也就是:
所以最终应该只选择:
而 只是循环过程中产生的候选值,并不会参与最终结果相乘。
快速幂循环中,x 会不断平方,依次表示:
同时,b 会不断右移,依次检查二进制的最低位是否为 1。
过程如下:
当前 b | 当前 x | b 是否为奇数 | ans 变化 |
|---|---|---|---|
| 13 | 31 | 是 | ans = 1 * 31 |
| 6 | 32 | 否 | ans 不变 |
| 3 | 34 | 是 | ans = 31 * 34 |
| 1 | 38 | 是 | ans = 31 * 34 * 38 |
因此最终:
也就是:
从二进制角度看,13 的二进制为 。从右往左依次检查每一位:
| 二进制位 | 当前 x | 是否乘入 ans |
|---|---|---|
| 1 | 31 | 乘入 |
| 0 | 32 | 不乘 |
| 1 | 34 | 乘入 |
| 1 | 38 | 乘入 |
所以代码中的:
if ((b & 1) == 1) { ans *= x;}这一步是在判断当前二进制位是否为 1。如果是 1,就把当前的 x 乘入答案。
而:
x *= x;b >>= 1;分别表示:
x *= x:让当前幂次翻倍,例如b >>= 1:让指数右移一位,继续检查下一位二进制
注意: 在 Java、C++ 等语言中,32 位有符号整数
int的范围是 。当 时,直接取相反数-n会导致溢出。解决方法是先将 存入 64 位整数(例如 Java 的long类型)再进行计算。
2.3 代码实现
class Solution { public double myPow(double x, int n) { // 0 次幂直接返回 1 if (n == 0) { return 1.0; } // 0 的正整数次幂直接返回 0 if (x == 0.0) { return 0.0; }
long b = n; double ans = 1.0; // 负指数先转成倒数,再把指数变为正数 if (b < 0) { x = 1.0 / x; b = -b; }
while (b > 0) { // 当前二进制位为 1,就把当前幂次乘进答案 if ((b & 1) == 1) { ans *= x; } // 当前幂次翻倍 x *= x; // 指数右移一位,继续处理下一位 b >>= 1; } return ans; }}2.4 复杂度分析
- 时间复杂度: ,循环次数与 的二进制位数成正比。
- 空间复杂度: ,仅使用常数大小的额外空间。
3. 方法二:递归快速幂(分治法角度)
3.1 算法原理
快速幂实际上也是分治思想的一种典型应用。
- 当 时,我们可以利用倒数关系将其转换为正数次幂:。
- 当 时,我们可以将问题划分为规模减半的子问题。设
half = dfs(x, n / 2),在 Java 中整数除法会自动向下取整,则有:
根据上式,我们只需要先递归地计算 ,然后根据 的奇偶性,决定是否多乘一个 即可。
3.2 递归过程示例:以 为例
上面的公式一开始可能不太直观,可以通过 来理解递归快速幂的执行过程。
递归快速幂的核心是:每次先计算一半的幂次,然后根据当前指数的奇偶性决定如何合并结果。
对于 :
由于 13 是奇数,所以:
只要先算出 ,就可以得到 。
继续拆分:
因为 6 是偶数,所以平方即可,不需要额外乘 3。
继续拆分:
因为 3 是奇数,所以平方后还需要额外乘 3。
继续拆分:
当指数为 0 时,递归到达边界,直接返回 1。
递归调用过程如下:
| 调用 | 含义 |
|---|---|
dfs(3, 13) | 计算 313,需要先计算 36 |
dfs(3, 6) | 计算 36,需要先计算 33 |
dfs(3, 3) | 计算 33,需要先计算 31 |
dfs(3, 1) | 计算 31,需要先计算 30 |
dfs(3, 0) | 指数为 0,返回 1 |
返回过程如下:
| 返回层 | 当前指数 | 计算方式 | 返回值 |
|---|---|---|---|
dfs(3, 0) | 0 | 返回 1 | 1 |
dfs(3, 1) | 1 | 1 × 1 × 3 | 3 |
dfs(3, 3) | 3 | 3 × 3 × 3 | 27 |
dfs(3, 6) | 6 | 27 × 27 | 729 |
dfs(3, 13) | 13 | 729 × 729 × 3 | 1594323 |
可以看到,递归快速幂并不是从 一步一步乘 13 次,而是不断把指数减半:
然后在返回时逐层合并结果。
因此,递归层数只和指数不断除以 2 的次数有关,时间复杂度为 。
- 递归边界: 当 时,返回 。若 且 ,直接返回 。这里默认题目不会额外考察 的负指数情况。

3.3 代码实现
class Solution { public double myPow(double x, int n) { // 0 次幂直接返回 1 if (n == 0) { return 1.0; } // 0 的正整数次幂直接返回 0 if (x == 0.0) { return 0.0; }
long b = n; // 负指数先在入口处处理,dfs 只负责非负指数 if (n < 0) { x = 1.0 / x; b = -b; }
return dfs(x, b); }
private double dfs(double x, long n) { // 非负指数的递归边界 if (n == 0) { return 1.0; }
double half = dfs(x, n / 2); // 偶数次幂:直接平方 if (n % 2 == 0) { return half * half; } // 奇数次幂:平方后再补一个 x return half * half * x; }}3.4 复杂度分析
- 时间复杂度: ,每次递归使幂次减半,因此递归树的深度为对数级别。
- 空间复杂度: ,递归树的深度为 ,需要占用对应大小的系统函数调用栈空间。
4. 算法对比与总结
| 方法 | 时间复杂度 | 空间复杂度 | 优缺点 |
|---|---|---|---|
| 方法一:迭代快速幂 | 推荐。避免了递归带来的系统栈开销,效率更高,更适用于工程实现。 | ||
| 方法二:递归快速幂 | 代码结构更贴近数学公式的直观表达,逻辑简单清晰,但存在栈溢出的隐患。 |
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时


















