【剑指Offer】数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
解法1
最直接的思路,计算base的exponent次方,则将base连乘exponent次即可,时间复杂度为O(exponent)
但是要注意处理特殊情况:
- 如果底数base等于0则直接返回0
- 非0数的0次方等于1
- 当指数为负数时的结果,相当于用1除以指数为正数时的结果
还有一个坑需要注意,下面的代码中使用了long y = exponent;
,需要将exponent转换为long类型
是因为exponent可以等于-2147483648(int类型的最小值),如果直接进行-exponent操作,由于int类型的最大值是2147483647,则会导致越界,出现错误的结果
实现代码
public double Power(double thebase, int exponent)
{
if(thebase == 0) return 0;
long y = exponent; // 避免越界
if(y < 0){
thebase = 1 / thebase;
y = -y;
}
double ret = 1;
for(double i = 0; i < y; i ++){
ret *= thebase;
}
return ret;
}
解法2
可以根据二分的思路,利用递归每次求指数的一半的次方结果,然后再将递归的结果相乘得到完整的指数次方
由于求指数的一半,即整数除以2的结果会自动向下取整,所以需要特殊处理指数为奇数时的情况,当指数为奇数时,需要在递归结果相乘的基础上再乘以一次底数
代码中使用的位运算e >> 1
相当于e / 2
来计算指数的一半
代码中通过位运算(e & 1) == 1
来判断指数是奇数还是偶数(奇数的二进制表示最低位一定是1,偶数的二进制表示最低位一定是0),相当于(e % 2) == 1
使用位运算会有更快的执行效率
实现代码
public double Power2(double thebase, int exponent)
{
if(thebase == 0) return 0;
if(exponent == 0) return 1;
long e = exponent; // 避免越界
if(exponent < 0){
e = - e;
thebase = 1 / thebase;
}
if(e == 1) return thebase;
double ret = Power2(thebase, (int)(e >> 1));
return (e & 1) == 1 ? thebase * ret * ret : ret * ret;
}
解法3
求解整数m的n次方,一般是mn = m * m * m .....,连乘n次,算法复杂度是O(n),这样的算法效率太低,我们可以通过减少相乘的次数来提高算法效率,即快速幂
对于n我们可以用二进制表示,以14为例,14 = 1110
可以发现这样的规律,指数n的二进制从低位到高位依次对应底数m的1次方,2次方,4次方,8次方...,当该二进制位是1的时候,则乘以底数对应的次方数,如果该二进制位是0,则不能乘以底数对应的次方数,即乘以1。
例如,m8对应的二进制位是1,所以最终结果中有m8
m1对应的二进制位是0,所以最终结果中只有m0,即1
使用快速幂后,原本需要的14次连乘,现在只需要4次连乘。
那么怎样得到一个整数的二进制位呢,又怎样判断该二进制位是0还是1呢
可以使用与运算和右移运算,例如对于14 = 1110
- 和1按位与得到0,即第一个二进制位是0
- 1110右移一位,得到0111,和1按位与得到1,即第二个二进制位是1
- 0111右移一位,得到0011,和1按位与得到1,即第三个二进制位是1
- 0011右移一位,得到0001,和1按位与得到1,即第四个二进制位是1
- 0001右移一位,得到0000,等于0则,算法结束
实现代码
public double Power3(double thebase, int exponent)
{
if(thebase == 0) return 0;
long y = exponent; // 避免越界
if(y < 0){
thebase = 1 / thebase;
y = -y;
}
double ret = 1;
while(y > 0){
if((y & 1) == 1){
ret *= thebase;
}
thebase *= thebase;
y >>= 1;
}
return ret;
}
更多算法题目的完整描述,AC代码,以及解题思路可以查看GitHub仓库Algorithm
原文地址:https://www.cnblogs.com/iwiniwin/p/12753447.html
- 老生常谈:利用Membership实现SSO(单点登录)
- nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录
- Mysql高效插入/更新数据
- 宋小菜融资2.3亿元!域名保护意识强
- 世界最奇葩的7款机器人
- Mysql高效插入/更新数据
- 关于Membership/Role您可能不知道的细节
- Sqlite向MySql导入数据
- 未来3年,人工智能如何影响法律行业?5位权威专家给出趋势
- Java 常见内存溢出异常与代码实现
- nginx限制上传大小和超时时间设置说明/php限制上传大小
- Unity Application Block 1.2 学习笔记
- 苹果首个自动驾驶专利到底有什么来头?
- 围棋遇上互联网:科技打开优秀传统文化未来之门
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Fence Repair (PKU 3253)
- JAVA 初学者的编码规范一:命名风格与代码格式
- Leetcode No.11 盛最多水的容器
- OpenCV图像拼接函数vconcat()&hconcat()
- JAVA自定义注解
- [ 物联网篇 ] 28 - Linux ES7210 Driver 调试
- [ 利器篇 ] - Microsoft Surface Pro 系列安装 Ubuntu 16.04 系统
- 如何优雅的打造 All-in One 仓库
- matplotlib绘图教程:设置标签与图例
- 企业是如何从头开发一个商业项目的?
- 基于Haproxy的高可用实战
- 组复制常规操作-分布式恢复 | 全方位认识 MySQL 8.0 Group Replication
- 赞!7000 字学习笔记,MySQL 从入到放弃
- 面试官问我Volatile的原理?从操作系统层面的设计怼回去!
- 设计原则之单一职责