Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
leetcode50:Pow(x, n)
一、题目描述:
实现 pow(x, n) ,即计算
x的n次幂函数(即,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= | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
|---|---|---|---|---|---|---|---|---|
| x^n= | - | x^64 | - | - | x^8 | x^4 | - | x^1 |
我们进行循环,对x不断的进行平方操作缓存起来cahce,当在得到x^1、x^4、x^8、x^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)
};