网站首页 > java教程 正文
前言
关于位运算,相信大家都不陌生,特别是写过一些对性能要求很严苛项目的同学,毕竟,这是一把提升程序性能效率的神兵利器。
我们都知道,程序中所有的数在计算机内存中都是以二进制的形式储存的,而位运算就是直接对整数在内存中的二进制位进行操作。比如,位与,位或,异或等。
本文着重于位运算的技巧总结,难度不会太大,掌握了还可以提高自己代码的逼格,但重点是要有耐心,理解位运算的作用。
正文
判断奇偶数
先看下,我们用高级语言提供的运算如何实现:
if (n % 2) == 0) {
// n 是个奇数
} else {
// n 是个偶数
}
如果把 n 以二进制的形式展示的话,其实我们只需要判断最后一个二进制位是 1 还是 0 就行了,如果是 1 的话,代表是奇数,如果是 0 则代表是偶数,所以采用位运算的方式的话,将待判断的整数与 1 做位与操作,比如说 8 (1000) & 1 ,结果为 0,说明其为偶数,看下代码实现:
if (n & 1 == 1) {
// n 是个奇数。
} else {
// n 是个偶数
}
看到这里,可能有人会说,编译器不是会帮我们优化成位运算吗,但是,但是,算了,可以直接到 IDE 里跑一下看看时间效率的对比,哈哈哈哈。
交换两个数
交换两个数应该有很多人都写过,但是应该都是需要借助一个临时的辅助变量,代码如下:
int tmp = x;
x = y;
y = tmp;
那么,你可以不用辅助变量来实现变量的交换吗?Let's see:
x = x ^ y // (1)
y = x ^ y // (2)
x = x ^ y // (3)
可能这里大家看了一脸懵吧(如果我猜想错了大家也别打我哈哈),还是解释一下,我们这里用的是异或运算,那么异或运算是什么呢,0 ^ 1 的时候结果才为 1,其余情况都为 0,所以异或运算有一个特性,是两个不相等的数做异或运算,结果为 1,那么
- 将 (1) 代入(2),得: y = x ^ y ^ y = x ^ 0,结果为 x
- 将 (1)(2) 代入 (3), 得: x = x ^ y ^ x = y ^ 0, 结果为 y
找出没有重复的数
这个题目我在 LeetCode 中有遇到过,其他数都出现了两次,只有一个出现一次:
给你一组整型数据,这些数据中,其中有一个数只出现了一次,其他的数都出现了两次,让你来找出一个数
我们看看题干,所有的数据,只有一个数只出现一次,而其他的都是出现两次,只要这两个重点信息就可以了,通过前面的题目我们已经了解了,异或运算在两个相同的数异或时结果为 0,而其他数跟 0 异或则为自己本身,那使用位运算的解法就昭然若揭了:
int find (int[] arr) {
int tmp = arr[0];
for(int i = 1; i < arr.length; i++){
tmp = tmp ^ arr[i];
}
return tmp;
}
m 的 n 次方
第一反应是啥,是不是这样?
int pow(int n){
int tmp = 1;
for(int i = 1; i <= n; i++) {
tmp = tmp * m;
}
return tmp;
}
确实是很常规的做法,效率也不是很高,但是想一想,Java 中的 Math.pow() 是不是也是这样实现的呢?答案是否定的,Math.pow() 是 native 修饰的方法,非 Java 实现,我也没有去看过具体的 C 代码,有兴趣的同学可以到 JDK 找到对应路径的类查看研究(感觉我好欠揍)。既然是 jdk 自己使用本地方法实现的,那效率肯定不会像上面那样(O(n)),要是那样,我们每个人都可以写 Java 语言了。那我们来看一下,使用位运算的实现思路:
int pow(int n){
int sum = 1;
int tmp = m;
while(n != 0){
if(n & 1 == 1){
sum *= tmp;
}
tmp *= tmp;
n = n >> 1;
}
return sum;
}
时间复杂度近为 O(logn),是不是效率上感觉提升了很多?离 jdk 大神又进了一步,哈哈哈哈~可能光看代码也要理解一段时间, 接下来加上文字描述,帮助大家理解代码,我们举个例子,比如 n = 15,则 n 的二进制表示为 1111,那么 m 的 15 次方可以拆解为: m ^ 1111 = m ^ 0001 * m ^ 0010 * m ^ 0100 * m ^1000, 然后我们可以通过 & 1和 >>1 来逐位读取 1101,为1时将该位代表的乘数累乘到最终结果。
找出不大于N的最大的 2 的幂指数
先看看传统做法:
int findN(int N){
int sum = 1;
while(true){
if(sum * 2 > N){
return sum;
}
sum = sum * 2;
}
}
再看看最大的 2 的幂指数,如果使用位运算的话要怎么使用,我们先看 2 的幂指数的二进制有什么特点,假设我们的数是 8 位二进制(整型一般是 32 位),那么 01000000, 00100000,00010000等等这些都是 2 的幂指数,即我们的目标,是要得到二进制数 1 的后面都为 0 的数。 目标有了,我们来看看实现的思路: 首先,我们把 n (假设为八位二进制)右边的数值全部变为 1
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
然后,再将 n + 1
n += 1;
最后,将 n 右移 1 位
n >> 1;
这样是不是就完成了找出一个数(比如 5 => 00000101,最大 2 的幂指数为 4)的最大 2 的幂指数的最大二进制数转化啦?附上完整代码:
int findN (int n) {
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
return (n + 1) >> 1;
}
总结
位运算呢,其实都是通过整型数转换成二进制去进行运算,我们需要对各种位运算的作用比较熟悉,然后拿到一道题,首先转换成二进制,看一下它有什么特点,再根据它的特点去拟运算,一开始可能会觉得比较困难,但是多看一些例子,多动手,慢慢地就能信手拈来啦~各位小伙伴加油 ~
作者:焯杰链接:https://juejin.im/post/5e3582cbe51d45580329c068
猜你喜欢
- 2024-10-18 Java 中的移位运算符(Shift Operator)
- 2024-10-18 「每日分享」把Map中的hash()分析的最透彻的文章
- 2024-10-18 从bitmap到布隆过滤器,再到高并发缓存设计策略
- 2024-10-18 Java基础入门(运算符)(java运算符的含义)
- 2024-10-18 Day03-Java运算(三年级混合运算练习题)
- 2024-10-18 浅谈java中的数学运算(java的数学公式有哪些)
- 2024-10-18 好程序员Java学习路线分享Java中的位移运算
- 2024-10-18 全网分析Map中hash方法最透彻的一篇文章
- 2024-10-18 为什么Java String哈希乘数为31?(java中string是什么数据类型)
- 2024-10-18 Java精确运算高位数数字(java计算精度问题)
你 发表评论:
欢迎- 07-15采用Oracle OSB总线进行服务注册和接入
- 07-15javaEE 新闻管理系统 oracle11+tomcat6
- 07-15从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 07-15如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 07-15【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 07-15【mykit-data】 数据库同步工具(数据库表同步工具)
- 07-15[Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- 07-15Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- 最近发表
-
- 采用Oracle OSB总线进行服务注册和接入
- javaEE 新闻管理系统 oracle11+tomcat6
- 从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 【mykit-data】 数据库同步工具(数据库表同步工具)
- [Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- Oracle数据库云服务系列新增前所未有的企业级功能
- 直播预告丨如何实现Oracle存储过程到java的一键转化
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)