1、常见位运算符
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0;有符号数,有的编译器补符号位(算术右移),有的补0(逻辑右移) |
1.1 与运算符 &
运算规则: 0&0=0 0&1=0 1&0=0 1&1=1
总结: 两位同时为1,结果才为1,否则结果为0。
注意: 负数按补码形式参加按位与运算。
用途:
-
清零
如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。 -
取一个数的指定位
比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。 -
判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)
来判断a是不是偶数。
1.2 或运算符 |
运算规则: 0|0=0 0|1=1 1|0=1 1|1=1
总结: 参加运算的两个对象只要有一个为1,其值为1。
注意: 负数按补码形式参加按位或运算。
用途: 常用来对一个数据的某些位设置为1
比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。
1.3 异或运算符 ^
运算规则: 0^0=0 0^1=1 1^0=1 1^1=0
总结: 参加运算的两个对象,如果两个相应位相同为0,相异为1。
性质:
-
交换律
-
结合律 (a ^ b) ^ c == a ^ (b ^ c)
-
对于任何数x,都有 x ^ x=0,x ^ 0=x
-
自反性: a ^ b ^ b = a ^ 0 = a;
用途:
-
翻转指定位
比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。 -
与0相异或,值不变
例如:1010 1110 ^ 0000 0000 = 1010 1110 -
交换两个数
void Swap(int &a, int &b){
if (a != b){
a ^= b;
b ^= a;
a ^= b;
}
}
1.4 取反运算符 ~
运算规则: ~1=0 ~0=1
总结: 对一个二进制数按位取反,即将0变1,1变0。
用途: 使一个数的最低位为零
使a的最低位为0,可以表示为:a & ~1。 ~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0。因为“ ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
1.5 左移运算符 <<
定义: 将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
1.6 右移运算符 >>
定义: 将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。
操作数每右移一位,相当于该数除以2。
2、应用
2.1 求和
题目
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
思路
用十进制做个比喻,计算十进制13+9:
-
计算不进位的和。十位1不变,个位3加9等于2,结果为12;
-
计算进位。十位没进位,个位进位为1,结果为10。
-
再计算十进制12+10:
- 计算不进位的和。十位1加1等于2,个位2加0等于2,结果为22;
- 计算进位。十位没进位,个位也没进位,结果为0。
-
因此结果13+9=22。
在二进制里
- 求不进位的和可以用异或运算,相同为0,不同为1
- 求进位可以用与运算,都为1才为1
代码
class Solution {
public int add(int a, int b) {
while(b!=0){
int array=(a&b)<<1;
a=a^b;
b=array;
}
return a;
}
}
2.2 只出现一次的数字
题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
- 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
思路
根据异或运算性质。
-
对于任何数x,都有 x ^ x=0,x ^ 0=x
-
自反性: a ^ b ^ b = a ^ 0 = a;
因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。
代码
class Solution {
public int singleNumber(int[] nums) {
int ans=0;
for(int num:nums){
ans^=num;
}
return ans;
}
}
2.3 求子集
题目: 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明: 解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路
假设nums=[1,2,3,4],二进制的0可以写成0000,代表一个数也不取,1=0001表示去第一个数也就是[1],2=0010,表示取第二个数[2],3=0011表示取1和2位[1,2],4=0100表示[3]…15=1111表示[1,2,3,4]
代码
public static List<List<Integer>> binaryBit(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
for (int i = 0; i < (1 << nums.length); i++) {
List<Integer> sub = new ArrayList<Integer>();
for (int j = 0; j < nums.length; j++)
if (((i >> j) & 1) == 1) sub.add(nums[j]);
res.add(sub);
}
return res;
}
2.4 求幂
题目
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
要求
- -100.0 < x < 100.0
- n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
思路
-
将n转换为二进制
以 x 的 10 次方举例。10 的 2 进制是 1010,x10=x(1010)2x^{10}=x^{{(1010)}_2}x10=x(1010)2 -
用 2 进制转 10 进制的方法把它展成 2 的幂次的和
x(1010)2=x1∗23+0∗22+1∗21+0∗20x^{{(1010)}_2}=x^{1*2^3+0*2^2+1*2^1+0*2^0}x(1010)2=x1∗23+0∗22+1∗21+0∗20 -
把n 的二进制表示中1的位置拿出来
比如 n 的第 i 位为 1,那么就将 xix^ixi 拿出来。x10=x1∗23∗x1∗21x^{10}=x^{1*2^3} *x^{1*2^1}x10=x1∗23∗x1∗21
代码
class Solution {
public double myPow(double x, long n) {
if(n<0) return 1.0/myPow(x,-n);
double res=1;
while(n!=0){
if((n&1)==1) res*=x;
x*=x;
n>>=1;
}
return res;
}
}