这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战
一、前言
大家好,本文章属于《剑指 Offer 每日一题》中的系列文章中的第 13 篇。
在该系列文章中我将通过刷题练手的方式来回顾一下数据结构与算法基础,同时也会通过博客的形式来分享自己的刷题历程。如果你刚好也有刷算法题的打算,可以互相鼓励学习。我的算法基础薄弱,希望通过这两三个月内的时间弥补这块的漏洞。本次使用的刷题语言为 Java ,预计后期刷第二遍的时候,会采用 Python 来完成。
- 刷题平台: leetcode 的剑指 Offer 专题
- 码云仓库地址
二、题目
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
示例1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例2:
输入:x = 2.10000, n = 3
输出:9.26100
示例3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
提示:
-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104
三、解题
3.1 思路1:递归
分奇数和偶数是为了能使 n 变小,知道递归的终止条件为0推出递归。偶数可以使 n 直接减少一半。奇数就继续 -1 计算。
3.1.1 代码
public static double myPow(double x, int n) {
if(n==0){
return 1;
}else if(n<0){
return 1/(x*myPow(x,-n-1));
}else if(n%2 ==1){
return x*myPow(x,n-1);
}else{
return myPow(x*x,n/2);
}
}
3.1.2 执行效果
3.1.3 复杂度
- 时间复杂度:O(logn)
- 空间复杂度:O(logn)
3.2 思路2:快速幂
这种解法真是想不到啊。快速幂的思想就是将指数化为二进制,然后快速相乘,极大的缩短遍历时间。
求 x^n 最简单的方法是通过循环将 n个 x 乘起来,依次求 x^1, x^2, ..., x^{n-1}, x^nx ,时间复杂度为 O(n). 快速幂法 可将时间复杂度降低至 O(log2 n).
例如 1011二进制数,从右至左分别为1 1 0 1 ,只有在1的位置上,才有相应的权重,这也就是为什么需要通过与运算:(b & 1) == 1判断最后一位是否为1。
另外的一个细节:因为32位int是补码形式,正数是和原码相同,范围是0到2的32次方-1,但是对于负数,需要反码+1,范围是2的32次方到0,负数要比正数多一个数字。如果传进来的int刚好是负2的32次方,取相反数之后就超过int32类型的取值范围了,所以需要用long来扩大取值范围。
3.2.1 代码
public static double myPow2(double x, int n) {
double res =0;
long t =n;
if(n<0){
t=-t;
}
while (t!=0){
// 最后一位为1 ,需要乘上该位上的权重。
if((t&1)==1){
res=res*x;
}
x*=x;
t=t>>1;
}
if(n<0) return 1/res;
return res;
}
3.2.2 执行效果
3.2.3 复杂度
- 时间复杂度:O(log2n),二分的时间复杂度为对数级别
- 空间复杂度:O(1),res,b等变量占用常数大小额外空间。
四、小结
本题一开始看着很简单,直接暴力循环处理即可,但是 Java 中提交上去居然超时了,然后一脸懵逼,然后学习到了上面的两种解题方法,都是非常的经典,第一种是递归法,考虑的很详细了,通过偶数的一半拆分来化解一半的复杂度,这种效率提升了一大步。此外,在算法题目中,也要考虑整数的负数溢出问题,位运算的效率比乘除法及取余法的效率要高上很多,这些都是代码需要优化的点,全是小细节,导致代码中的程序效率千差万别。
方法二的快速幂是非一般人能想到的,没有一定的刷题经验和数学知识,谁会往这方面想,只能做个记录,希望下次能够想起来。
今天的刷题就到这了,欢迎点赞评论交流,剑指 Offer 刷题之旅将继续展开!