快速幂(迭代+递归)

332 阅读1分钟

Pow(x,n)

image.png

题目分析

  • 这道题我们采用分治思想来解决,对于求解xnx^n我们可以先求出xn/2x^{n/2}然后相乘得到xnx^n。以此类推xn=(xn/2)2=((xn/4)2)2=...x^n=(x^{n/2})^2=((x^{n/4})^2)^2=...,这是n为偶数的情况,如果n为奇数我们需要对xn/2x^{n/2}再乘以xx,还要考虑一些特殊值以及数字越界的情况。

递归(代码)

var myPow = function(x, n) {
    if (x === 0) return 0;
    if (x === 1 || n === 0) return 1;
    const logic = (b, i) => {
        if (i === 1) return b;
        return logic(b, i >>> 1) ** 2 * (i & 1 ? b : 1);
    }
    return n < 0 ? 1 / logic(x, -n) : logic(x, n);
};

题解

  • 函数入口处我们首先对一些特殊值进行处理之后直接返回,然后开始进行递归求值,递归的终止条件为i==1i==1
  • 如果nn为负数,我们应该返回1/logic(x,n)1/logic(x,-n)的计算结果。
  • 题目中231<=n<=2311-2^{31}<=n<=2^{31}-1,当n=231n=-2^{31}时我们会向logiclogic中传入2312^{31},这时如果我们使用有符号右移>>>>就会陷入死循环。在js中所有的位运算都会先把运算数转换成有符号32位整型,超过32位的整数会被截断,小数部分会被舍弃。这里2312^{31}已经超过js有符号32位整型的最大表示数,所以无法得到正确的计算结果。
  • 对于这种情况我们采用无符号右移>>>>>>来解决,该操作符不会考虑符号位,左侧会直接用0进行填充。

迭代(代码)

var myPow = function (x, n) {
    if (x === 0) return 0;
    if (x === 1 || n === 0) return 1;
    let result = 1;
    if (n < 0) {
        x = 1 / x;
        n *= -1;
    }
    while (n !== 0) {
        if (n & 1) {
            result *= x;
        }
        n >>>= 1;
        x *= x;
    }
    return result;
};

题解

  • 首先还是进行特殊值的处理。
  • 如果n<0n<0我们需要计算的就是(1/x)n(1/x)^{-n}
  • 循环的终止条件是n=0n=0,需要注意的是当nn为奇数是需要对resultresult乘上一次xx

总结

  • 迭代相对于递归的优势在于少了函数调用栈的开销,但是代码相对于递归没有那么清晰明了。