最近深感看过或学习过的知识极易忘记,所以想在下班后对当天或是最近学到的知识做一点总结记录,希望能坚持。
首先,之所以记录位运算是因为最近在leetcode上刷题练习时遇到这样一道题目:Sum of Two Integers.
Calculate the sum of two integers a and b, but you are not allowed to use the operator +
and -。
题目的意思很明确,即不使用+和-计算两个Integers的和,由此题引起了我对关于位运算的回忆。
首先,java中的位运算符有7中,即与&、或|、非~(并不是!)、异或^、左移<<、右移>>、无符号右移>>>。当然,以上操作都是针对二进制位运算的。
记住定义往往是十分枯燥的,不如几个例子来得实在。
5|4=0101|0100=0101=5
5&4=0101&0100=0100=4
^顾名思义(相异或者为假)
5^4=0101^0100=0001=1
<<和>>以及>>>也是见名知意
5<1=0101<1=1010 5>1=0101>1=0010
>>>和>>的区别在于负数的情况
int a=-1;
源码:0000 0000 0000 0000 0000 0000 0000 0001
反码:1111 1111 1111 1111 1111 1111 1111 1110
补码:1111 1111 1111 1111 1111 1111 1111 1111(反码+1)
a>>2: 1111 1111 1111 1111 1111 1111 1111 1111(考虑符号位,所以右移两位后高位用两个1补齐)
a>>>2:0011 1111 1111 1111 1111 1111 1111 1111(不考虑符号位,直接以0补齐)
突然想到曾经被问某数除以8的最快方法,其实就是右移3位,哎当时没反应过来。右移以为即相当于除以2,左移一位相当于乘2,且位运算要远快于乘除运算。
说回到之前遇到的那道不使用运算符计算加减法,其实加减法的本质就是二进制的位运算呗。
5+4=0101+0100=1001=9
在上面的运算过程中,实际上是对应位置的0与1的相加,结果也只有0和1两种情况。首先可以明确的是1与0对位一定为1,0和0仍为0,1与1为1并产生一个进位。
有了这个认识基础,实际上可以分两步考虑了。首先,直接两数做一个^运算,0101^0100=0001,得到的结果就是没有操作进位的结果,而进位的操作方法也很明显,
即做一个与运算,0101&0100=0100,0100左移一位1000即我们需要的进位数,此数和前面^得到的结果相加不就是我们要的结果吗?当然不允许直接相加,这又回到
了第一步,实际上是个递归的过程,一直这样操作到进位为0则结果得出。具体代码如下
public static int getSum(int a, int b) {
int carry;
while (b != 0) {
carry = a & b;
a = a ^ b;
b = carry << 1;
}
return a;
}