LeetCode 50. Pow(x, n):从 O(n) 到 O(log n) 的快速幂彻底搞懂

题目是实现一个函数 pow(x, n),计算 xnx^nxn。约束里 n 可以到 ±231−1\pm 2^{31}-1±231−1,如果用最朴素的「循环 n 次相乘」,时间复杂度是 O(∣n∣)O(|n|)O(∣n∣),在这个范围明显不可接受。leetcode

这篇就从「最直观的数学思路」开始,一步步推到快速幂,让你真的明白为什么是 O(log⁡n)O(\log n)O(logn)。


朴素做法:按定义来,O(n)

最直观的定义是:

xn=x⋅x⋅x⋯x⏟n 个x^n = \underbrace{x \cdot x \cdot x \cdots x}_{n\ \text{个}}xn=n 个x⋅x⋅x⋯x​​

实现思路:

  • 准备一个 result,从 1 开始。

  • 循环 n 次,每次乘上一个 x。

这个做法的特点:

  • 思路简单,完全符合直觉。

  • 时间复杂度是 O(∣n∣)O(|n|)O(∣n∣)。当 ∣n∣|n|∣n∣ 接近 10910^9109 时,完全跑不动。leetcode

所以我们需要换一个角度看「幂」,而不是一口一口「吃 x」。


换个视角:把 n 拆成若干个 2 的幂

关键想法不是「乘 n 个 x」,而是「用几块大积木拼出 xnx^nxn」。

积木的规格只有这些:

x1, x2, x4, x8, x16, x32, ...x^1,\ x^2,\ x^4,\ x^8,\ x^{16},\ x^{32},\ \dotsx1, x2, x4, x8, x16, x32, ...

也就是所有 x2kx^{2^k}x2k 这种形式。

数学上的事实:

任何正整数 n,都可以唯一写成若干个不同的 2 的幂之和。

例如:

  • 13=8+4+113 = 8 + 4 + 113=8+4+1

  • 10=8+210 = 8 + 210=8+2

  • 7=4+2+17 = 4 + 2 + 17=4+2+1

于是:

  • x13=x8+4+1=x8⋅x4⋅x1x^{13} = x^{8+4+1} = x^8 \cdot x^4 \cdot x^1x13=x8+4+1=x8⋅x4⋅x1

  • x10=x8+2=x8⋅x2x^{10} = x^{8+2} = x^8 \cdot x^2x10=x8+2=x8⋅x2

  • x7=x4+2+1=x4⋅x2⋅x1x^7 = x^{4+2+1} = x^4 \cdot x^2 \cdot x^1x7=x4+2+1=x4⋅x2⋅x1

这就是快速幂的核心:只要我能高效拿到这些「规格为 2k2^k2k 的幂积木」,再选对几块乘起来,就能得到 xnx^nxn


base 和 result:谁负责什么?

快速幂里有两个核心变量:

  • base:当前这一轮代表的「幂积木」。

  • result:到目前为止已经选中的那些积木的乘积,也就是部分结果。

职责分工:

  • base 从 x1x^1x1 开始,每一轮变为自己的平方:

    • 第 1 轮:base = x1x^1x1

    • 第 2 轮:base = x2x^2x2

    • 第 3 轮:base = x4x^4x4

    • 第 4 轮:base = x8x^8x8

    • ...

    第 k 轮的 base 是 x2k−1x^{2^{k-1}}x2k−1。

  • result 一开始是 1(什么都没选)。

    在某一轮,如果当前这块积木是「需要的」,就做:

    result∗=base\text{result} \mathrel{*}= \text{base}result∗=base

否则就跳过。

可以用一句话记住这点:

base 在「生产积木」,result 在「选择积木」。

base 可以每轮平方,result 不能每轮随便平方。

只要这句不忘,就不会写出错误的实现。


怎么知道当前这块积木要不要选?

引入一个变量 e 表示「还没处理完的指数」,一开始 e = |n|。

每一轮做三件事:

  1. 看 e 是否为奇数:

    • 如果 e 是奇数,说明目前的指数里有一块「单独的 1 次幂」需要用,这一块恰好对应当前 base:

      • result *= base
  2. 把能成对的部分合并:

    • base *= base(利用 x2k=(xk)2x^{2k} = (x^k)^2x2k=(xk)2,把成对的幂打包成新的积木)
  3. 减少还没处理的指数:

    • e /= 2(向下取整,相当于把已经处理完的这一位丢掉)

循环到 e 变成 0 为止,所有需要的积木都已经选完,result 就是 x∣n∣x^{|n|}x∣n∣。


用 x13x^{13}x13 走一遍

以 n = 13 为例,13 的分解是 13=8+4+113 = 8 + 4 + 113=8+4+1,我们应该选的积木是 x1,x4,x8x^1, x^4, x^8x1,x4,x8。

初始化:

  • base = x

  • result = 1

  • e = 13

按轮数走:

轮次 e 进入时 e 奇偶 base 进入时 操作 result 出来时
1 13 奇数 x1x^1x1 result = base x1x^1x1
base = base² = x2x^2x2
e = 13 / 2 = 6
2 6 偶数 x2x^2x2 跳过 result x1x^1x1
base = x4x^4x4
e = 6 / 2 = 3
3 3 奇数 x4x^4x4 result = base x5x^5x5
base = x8x^8x8
e = 3 / 2 = 1
4 1 奇数 x8x^8x8 result = base x13x^{13}x13
base = x16x^{16}x16
e = 1 / 2 = 0

可以看到:

  • 用到的 base 是 x1,x4,x8x^1, x^4, x^8x1,x4,x8,刚好对应 13=1+4+813 = 1 + 4 + 813=1+4+8。

  • 一共 4 轮,复杂度是 O(log⁡13)O(\log 13)O(log13)。


为什么不能"每轮都乘一次 base"?

假设循环了 k 轮,base 依次是:

x1,x2,x4,x8,...,x2k−1x^1, x^2, x^4, x^8, \dots, x^{2^{k-1}}x1,x2,x4,x8,...,x2k−1

如果你每一轮都做 result *= base,那 result 会变成:

x1+2+4+⋯+2k−1=x2k−1x^{1+2+4+\dots+2^{k-1}} = x^{2^k - 1}x1+2+4+⋯+2k−1=x2k−1

问题:

  • 指数固定是 2k−12^k - 12k−1,只能算出 1, 3, 7, 15, 31...,不能覆盖一般的 n。

  • 完全没用上「n 的具体值」,只看了循环跑了几轮。

所以在快速幂的框架里:

"每轮都更新 result" 在数学上就是错的,等于把所有积木都拿了,而正确做法是像凑钱一样,只拿能刚好拼出 n 的那几张硬币。


负指数和边界情况

完整实现里还要处理两个细节。

1. 负指数

如果 n < 0,可以先计算 x−∣n∣x^{-|n|}x−∣n∣,最后取倒数:

xn=x−∣n∣=1x∣n∣x^n = x^{-|n|} = \frac{1}{x^{|n|}}xn=x−∣n∣=x∣n∣1​

实现上的常见处理:

  • 记录 isNegative = (n < 0)

  • 用 64 位整型保存 absN,避免最小 32 位整数取反溢出。

  • 循环里只处理非负指数 absN,最后如果 isNegative 为真,返回 1 / result。leetcode

2. n = 0

任何非 0 数的 0 次幂是 1,直接返回 1 即可。leetcode


算法小结

快速幂可以用一句话总结:

用 base 依次生成 x1,x2,x4,x8,...x^1, x^2, x^4, x^8, ...x1,x2,x4,x8,... 这些「2 的幂次积木」,用 result 按照指数 n 的拆分结果,挑出其中若干块相乘;每一轮都把指数缩小一半,所以整体复杂度是 O(log⁡∣n∣)O(\log |n|)O(log∣n∣)。leetcode

更程序化地描述就是:

  1. 处理 n = 0,直接返回 1。

  2. 如果 n < 0,先把 n 变为正数,记住最后要取倒数。

  3. 初始化:

    • result = 1

    • base = x

    • e = |n|

  4. 当 e > 0:

    • 如果 e 是奇数,result *= base

    • base *= base

    • e /= 2

  5. 如果原来的 n 为负,返回 1 / result,否则返回 result。leetcode

理解了「积木」和「硬币凑钱」这两个类比之后,快速幂就不再是一个需要死记硬背的模板,而是一个非常自然的数学过程。

相关推荐
@小码农6 小时前
2026年信息素养大赛【星火征途】图形化编程复赛和决赛模拟题B
开发语言·数据结构·c++·算法
人道领域6 小时前
【LeetCode刷题日记】347.前k个高频元素
java·数据结构·算法·leetcode
七颗糖很甜6 小时前
台风数据免费获取教程
大数据·python·算法
AI科技星6 小时前
《全域数学》第一部·数术本源
算法·机器学习·数学建模·数据挖掘·量子计算
天真小巫6 小时前
2026.4.30总结
职场和发展
阿Y加油吧6 小时前
二刷 LeetCode:118. 杨辉三角 & 198. 打家劫舍 复盘笔记
笔记·算法·leetcode
深邃-6 小时前
【数据结构与算法】-二叉树(1):树的概念与结构,二叉树的概念与结构
数据结构·算法·链表·二叉树··顺序表
风筝在晴天搁浅7 小时前
手撕归并排序
数据结构·算法·排序算法
lynnlovemin7 小时前
C++高精度加减乘除算法详解
开发语言·c++·算法·高精度