说起位运算,大家想的应该不会陌生,学计算机的多多少少会有一些接触,但是在实际编程中用的很少,因为我们想不到位运算有什么用,我只是在参看Java源代码时,才会看到位运算,但是最近刷力扣题之后,我忽然惊讶,位运算还有这样的用法啊,感觉真的很神奇,现在我把做的一些题总结一下。这里就不再介绍运算的使用了。
目录
我们用做题的方式来学习,这样效率高,而不是死记硬背。
题目一:如何交换两个数?
大家想的首先就是,定义一个变量作为交换的容器,像这样:
int x=3;
int y=4;
int temp;
temp=x;
x=y;
y=temp;
但是如果要求不使用变量temp,应该怎么做?当时我就在想,不使用变量temp怎么可以可能交换呢,后来看到题解,突然就明白了。我们可以这样写:
int x=3;
int y=4;
x=x^y;
y=x^y;
x=x^y;
没错,就是使用亦或运算,先用x存储3和4的亦或结果
然后将3和4亦或的结果,再和3亦或就得到了4
同理将3和4亦或的结果,再和4亦或就得到了3。神奇吗?哈哈反正我觉得挺神奇的。
下面继续以题目为例学习位运算的奇巧淫技
题目二:求二进制中1的个数
这道题对大家来说,应该不算难,常规思路就是把这个数转化为二进制,在转化为二进制的同时判断有多少个1。既然我们讲到位运算,那就来看看使用位运算怎么实现。
以15为例,15的二进制为
如何获取第一个1呢?我们可以这样
将15的二进制和1进行&运算,这样就得到了15的第一个数,判断这个数是不是即可,但是想要获得15第二位数怎么办?我们能想到位运算中的>>运算,如图
就这样一直右移,直到15变为0。但是要循环多少次呢?我们知道int类型的数据占四个字节,一个字节占8位,所以一共要循环32次。有了这个思路写代码就很简单了,如下:
public static int count(int x) {
int count=0;
int i=0;
while(i<32) {
if(((x>>i++)&1)==1)
count++;
}
return count;
}
这个题还有一个解法更快,思考一下,上一个解法我们要循环32次,但是我们有必要循环32次吗?有的循环就是多余的,有没有方法解决有多少1循环多少次呢?
下面以15为例,来讲解&运算的秒用,如图:
每次都让num&(num-1),这个作用是消除一个1,每次循环消除一个1,直到num为0停止循环。要好好体会一下,代码 如下:
public static int count(int x) {
int count=0;
while(x!=0) {
x=x&(x-1);
count++;
}
return count;
}
题目三:不使用判断和编程语言中内置函数将负数变为正数
不使用判断将负数变为整数,我们只能考虑使用位运算,我们需要知道负数在计算机中是如何存储的,在计算机中如何区分正负数?其中最高代表的符号位。最高位为0,代表正数。最高位是1,代表负数。这是规定,记住就可,
二进制负数的反码: 将最高位的符号位以外的数,全部取反,这样就得到了负数的反码。
负数的补码:将负数的反码进行加一操作,得到的结果便是负数的补码。
例如:以-5为例,int类型一共32位,太多我只写8位,懂原理即可
-5的二进制原码为:1000 0101
-5的二进制反码为:1111 1010
-5的二进制补码为:1111 1011
将负数变为整数,我们只需要得到最高位,我们能想到的就是>>>右移,为什么不是>>呢?看一下它们的区别:
- >>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;
- >>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。
代码如下:
public static int abs(int x) {
return (1-((x>>>31)<<1))*x;
}
关于二进制的巧用并不只这么多,例如
- 用一条语句判断一个整数是不是2的整数次方
- 一个数组里除了某一个数字之外,其他的数字都出现了两次。请编写程序找出这个再出现一次的数字
这里不再给出题解,相信大家有了前面的学习能很快的想出思路。
总结
不要认为自己笨,想不出来,其实第一次做这种题,想不出来很正常,这种时候你应该说,哇!好神奇,而不是责备自己,只有不断的接受,才能突破了自己的知识盲区,很多时候知识盲区要靠见多识广,而不是靠自己发明创造。