每日一题:二分法相关题目Pow(x,n)

149 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

leetcode50:Pow(x, n)

一、题目描述:

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

示例:

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

示例:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2^(-2) = (1/2)^2 = 1/4 = 0.25

二、解题思路:

1. 使用普通方法累乘,当n<0时就是累除,或1/x的累乘

var myPow = function(x, n) {
    let res=1; // 任何x的0次方都为1
    if (n<0) {
        x = 1/x
        n = -n
    }
    for (let i=1;i<=n;i++){
        res = res*x
    }
    return res
};

值得注意的是,在leetcode中提交该代码,会有一个测试用例:

输入:x = 1.00000,  n = -2147483648

此时n的量级超大,此时循环次数过多运行超时测试不通过。

所以我们需要用二分法,将O(n)的复杂度降为log2(n),在n很大时,差异极为明显。

2. 使用二分法+递归:

x^77为例:

x^77 = x^38 * x^38 * x  //当77整除2有余数时需要额外补充一个x
x^38 = x^19 * x^19      //可以整除2则不做额外处理
x^19 = x^9 * x^9 * x
x^9 = x^4 * x^4 * x
x^4 = x^2 * x^2
x^2 = x * x

我们可以使用递归法

var myPow = function(x, n) {
    // 二分法+递归;复杂度:时间logn + 空间logn(即递归的层数)
    function loop(x, n) {
        if (n===0) {
            return 1
        }
        // 递归调用
        let y = loop(x, parseInt(n/2))
        // 单层的逻辑
        return n%2==0 ? y*y : y*y*x
    }
    return n>0 ? loop(x,n) : 1/loop(x,n)
}

3. 使用二分法+迭代

还是以x^77为例:我们试着找规律,看看哪些地方需要额外乘x。我们用+号表示需额外*x

x -> x^2 -> x^4 -> (+)x^9 -> (+)x^19 -> x^38 -> (+)x^77
在x^77的地方,需要额外乘x。(一次二分相当于x^2)
在x^19的地方,因为经历了两次二分,倍数应该是2^2,所以最终相当于乘了x^(2^2) = x^4
在x^9的地方,经历了三次二分,倍数应该是2^3,所以相当于乘了x^(2^3) = x^8
其他没有额外乘x的情况,经历了6次二分,所以贡献累乘的x数量=x(2^6)= x^64
x^64 * x * x^4 * x^8 = x^77

这些x的指数1 4 8 64恰好就对应了 77 的二进制表示 (1001101)中的每个 1!我们可以对x^n中的n进行二进制拆分

77=01001101
x^n=-x^64--x^8x^4-x^1

我们进行循环,对x不断的进行平方操作缓存起来cahce,当在得到x^1x^4x^8x^64的时候,即此时当前位为1时,将这些值累乘到结果res。具体看下列代码:

var myPow = function(x, n) {
    function loop(x,num) {
        let res = 1, cache = x;
        while(num>0) {
            if (num % 2 == 1) {    // n按位与1时即为判断末位是否为1
                res*=cache	// 将当前平方的缓存累乘到结果res
            }
            cache*=cache
            num = parseInt(num/2)  // 每次循环n右移1位(相当于除以2),以便判断末尾为1时进行累乘
        }
        return res
    }
    return n>0 ? loop(x,n) : loop(1/x,-n)
};