前言
有一天你要去面试,你信心满满,什么数据库索引优化,jvm内存管理你都倒背如流。和你一起面试的还有一个看上去非常年轻像毕业的学生,你对他不屑一顾。见了面试官,你已经准备好了应付他的难题,只见面试官微微一笑:“请问现在有两个int变量a,b,在不创建新变量的情况下,如何交换他们的值呢?”你有点懵逼,在纸上思前上去也毫无头绪,难道不创建新变量还能交换值?只见那位学生微微一笑,提笔写了几下便交给面试官,只见面试官满意的点点头,而你却尴尬地坐在原地(欲知后事请往下看)
1.什么是位运算?
首先,我们要知道,我们地计算机存储数据的形式都是二进制。就是所有的东西他都是转化位二进制来保存,而二进制只有0和1两个数字,我们的位运算就是在二进制的基础上进行运算,它是一个二元运算符,就是对两个数据进行操作。它是编程语言中最难最复杂的运算,所以专门用一篇文章讲解。如果你还没了解其他的运算,推荐看看我的这篇文章,非常适合新手
搞定编程语言所有基础运算(图解,详细代码)
2.位运算有那些?
1.按位与&
按位与的符号与我们逻辑运算符的逻辑与是一样的,都是&。那&啥适合是按位与啥时候又是逻辑与呢?当我们&左右两边是整型变量时,它代表的是按位与。当左右两边是布尔值时,它是逻辑与。 1&1=1 1&0=0 0&0=0
按位与&的逻辑是:如果两个对应的二进制位都位1,则该位的结果值为1,否则为0
通过下图能直观的表示。
PS:我们知道在逻辑与中两个true才是真的true,而在代码中,往往1代表true,0代表false。所以两个1代表真的1,这样方便我们记忆。
2.按位或 |
按位或 | 符号与我们的的逻辑或符号相同,都是 | 。它的意思和我们的按位与相反。当左右两边只要有一个1则就为1,同理与上面的记法。或者你可以记成按位与&是都为1才为1,按位或|是都为0才为0。 1|1=1 1|0=1 0|0=0
同样通过一个图进行理解:
3.异或^
异或的符号是^,一般按键盘的shift加6,如果输出的是……这样则需要切换成英文键盘。异或的规则是两个位数相同则位0,否则位1。因为它的名字带一个异,而异的意思就是不同,说明如果是不同的话就说明是真的那就是1,都一样的话那就是相同的,则是假的,记为0. 1^1=0 1^0 =1 0^0=0
前言解答:
你拿过纸上看了看,只见上面简便地写着:
public static void main(String[] args) { int a=10; int b=12; a=a^b; b=a^b; a=a^b; System.out.println(a);//12 System.out.println(b);//10 }
你惊讶地看着这短短地代码,于是在纸上演算了起来
解析:我们可以发现,12和10异或得到一个数6,这个6和他们之间任何一个再次异或后可以得到另外一个。我们暂时将这个数6成为中转数,即6再和12异或又得回了10,再和10异或又得回12,这三个数互相异或都会得到另外一个。这个性质就产生了前言的替换功能,没有借助多余的变量,是不是很神奇
4.取反运算符~与左右移运算符<< >>
- ~
- 取反
- ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1
- <<
- 左移
- 用来将一个数的各二进制位全部左移N位,右补0
- >>
- 右移
- 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0
解析:这三个位运算用的较少,不做过多解释,不过大家可以记住:一个整数左移一位会翻两倍,移动两位翻四倍,如10左移一位变20,移两位变40。向右移动则相反,移动一位缩小一半,以此类推。
3.位运算的技巧(干活)
1.干货一:消去X二进制位的最后一位1
x=1100(二进制形式)
x-1=1011
x&(x-1)=1000
x二进制的最后一位1变为了0
2.快速判断是否是2的幂。
首先我们要明白,如果X是2的幂,如1,2,4,8,16等等
1.X>0
2.X的二进制的表示中只可能有一个1
如果我们利用x&(x-1)去掉x唯一的一个1,则得到结果肯定为0
如果,只要a是2的幂那么得到的结果肯定为0
public class Demo5 { public static void main(String[] args) { int a=16; int b=a&(a-1); System.out.println(b);//0 } }
3.判断一个数的二进制位有几个1
这是一个高频的考点,同理于上面,如果我们利用x&(x-1)可以去掉最后一位0,我们通过while循环一直除去最后一位0,直到除完以后x变位0,则知道x的二进制位有几位1。
代码演示:
public class Demo5 { public static void main(String[] args) { int a=345897; int count=0; while (a!=0){ a=a&(a-1); count++; } System.out.println(count);//9 } }
4.找出重复数组中,唯一一个不重复的数字
题目:在一个数组中,每个数字都出现了两次,只有一个数组出现了一次,请找出这个数字
int[] arr={1,1,2,2,3,4,4,5,5,6,6}
我们通过异或的性质可以很清楚理解:x^x=0 x^0=x
public class Demo5 { public static void main(String[] args) { int[] arr={1,1,2,2,3,4,4,5,5,6,6}; int x=0; for (int i = 0; i < arr.length; i++) { x=arr[i]^x; } System.out.println(x);//3 } }
第四个是LeetCode的题目,它还有升级版的题目,有兴趣的可以去搜索,在这由于篇幅有限不做叙述。