全栈开发知识库

106.Pow(x, n)

题目

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数。

示例 1:

  • 输入:x = 2.00000, n = 10
  • 输出:1024.00000

示例 2:

  • 输入:x = 2.10000, n = 3
  • 输出:9.26100

示例 3:

  • 输入:x = 2.00000, n = -2
  • 输出:0.25000

思路

阶乘就是要拿n个x来连乘。
但我们不一个一个的乘,而是把他们成倍成倍的叠起来。

比如算 3^13。 如果硬算,就是:
3 × 3 × 3 × 3 × 3 × 3 × 3 × 3 × 3 × 3 × 3 × 3 × 3
太慢了。
我们换个办法: - 3^1 = 3 - 3^2 = 3 × 3 = 9 - 3^4 = 9 × 9 = 81 - 3^8 = 81 × 81 = 6561 你看出来了吧,每次都把前面的结果自己乘自己,指数就翻倍了。 所以: - 1 变 2 - 2 变 4 - 4 变 8 - 8 变 16 然后看 13 需要哪几块:
13 = 8 + 4 + 1
所以:
3^13 = 3^8 × 3^4 × 3^1
也就是说,不是每一层都要用,只把需要的那几层拿出来乘。

为什么要 while (n > 0)

因为我们要一直把 n 拆下去,直到拆没了。

每次都做这三件事:
- 如果 n 是奇数,说明当前这一层要用,先乘进答案 - 把底数自己乘自己,下一层就翻倍了 - 把 n 除以 2,继续看下一层 等 n = 0 了,就说明没有层可以拆了,结束。

为什么奇数要乘进答案 因为奇数说明这一层“多出来一份”。 比如: - 13 可以拆成 8 + 4 + 1 - 最后那个 1 就表示当前这一层要拿一次 所以只要看到 n 是奇数,就把当前 base 乘进结果。

class Solution {
    fun myPow(x: Double, n: Int): Double {
        // 用 Long 存指数,避免 n = Int.MIN_VALUE 时取反溢出
        var exp = n.toLong()

        // 当前要不断“翻倍”的底数
        var base = x

        // 最终答案,先设成 1,因为 1 乘任何数都不影响结果
        var result = 1.0

        // 如果指数是负数,先把问题变成正数指数
        // 例如 x^-2 = (1 / x)^2
        if (exp < 0) {
            base = 1.0 / base
            exp = -exp
        }

        // 只要指数还没变成 0,就继续拆
        while (exp > 0) {
            // 如果当前指数是奇数,
            // 说明这一层的 base 要算进结果里
            if (exp % 2L == 1L) {
                result *= base
            }

            // 底数自己乘自己,进入下一层
            // 3 -> 9 -> 81 -> 6561 ...
            base *= base

            // 指数减半
            exp /= 2
        }

        return result
    }
}

复杂度

  • 时间复杂度: n 每次砍半,所以是 O(log n)
  • 空间复杂度: 只用几个变量,所以是 O(1)


  • 邮箱 :charon.chui@gmail.com
  • Good Luck!
本页由 Markdown 自动渲染 · 共 628 篇笔记