位运算在日常开发中不是很常用,但是巧妙的使用位运算可以大量减少运行开销,优化算法。因此在算法中为了减少开销,算法的复杂度有要求的情况下,位运算会起到意想不到的效果。
位运算
1.Java支持的位运算符
&:按位与。
|:按位或。
~:按位非。
^:按位异或。
<<:左位移运算符。
“>>”:右位移运算符。
<<<:无符号右移运算符。
1)按位与 &
规则总结:只有两个操作数对应位同为1时,结果为1,其余全为0. (或者是只要有一个操作数为0,结果就为0)。
2)按位或 |
规则总结:只有两个操作数对应位同为0时,结果为0,其余全为1.(或者是只要有一个操作数为1,结果就为1)。
3)按位非 ~
4)按位异或 ^
5)左位移 <<
符号位不变,低位补0。
2:10
2<<2:1000 =8
M << n = M * 2n
6)右位移 >>
低位溢出,符号位不变,并用符号位补溢出的高位。
如:-6>>2结果为-2。
这里总结一下,关于负数或者正数来说,移位的时候是一样的,但是在补位的时候,如果最高位是0就补0,如果最高位是1就补1
由此我们得出一个快速的算法 M >> n 其实可以这么算 M >> n = M / 2n
力扣关于位运算的题
1)剑指offer65:不用加减乘除做加法。
题目:写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
解题思路:设两数字的二进制形式a,b,其求和s=a+b。
无进位和n与进位c的计算公式(适用于正数和负数的加法)
s=(非进位和n)+(进位c). s=a+b=>s=n+c 循环求n,c直到进位c=0,s=n (这样就不用加法了)
算法代码:
class Solution {
public int add(int a, int b) {
while(b!=0){ //进位等于0跳出
int c =(a&b)<<1; //进位
a=a^b; //非进位和
b=c; //进位b
}
return a;
}
}
2)剑指offer56-Ⅰ:数组中数字出现的次数。
题目:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
解题思路:异或运算有个重要的性质,两个相同数字异或为0,对任意整数a⊕a=0。
本题难点:数组nums有两个只出现一次的数字,无法通过异或直接得到
两个只出现一次的数字为x,y,由于x不等于y,则x和y二进制至少有一位不同
- 遍历nums执行异或
- 循环左位移计算m,找到x⊕y某为1的二进制位;初始化一个辅助变量m=1,通过与运算从右向左循环判断,可获取整数x⊕y首位1,记录于m中。
- 拆分nums为两个子数组
- 分别遍历两个子数组执行异或(通过遍历判断nums中各数字和m做与运算)
- 返回
算法代码:
class Solution {
public int[] singleNumbers(int[] nums) {
int x=0,y=0,n=0,m=1;
for(int num : nums){
n^=num; //遍历nums执行异或
}
while((n&m)==0){ //循环左位移计算m
m<<=1;
}
for(int num : nums){ //拆分nums为两个子数组
if((num&m)!=0) x^=num; //当num&m!=0
else y^=num; //当num&m==0
}
return new int[] {x,y};
}
}