位运算(Java)
1.位运算基础
Java中常用位运算符号包括:
操作符 | 名称 | 描述 |
---|---|---|
& | 与 | 如果对应位都是1,则结果为1,否则为0 |
| | 或 | 如果对应位都是0,则结果为0,否则为1 |
^ | 异或 | 如果对应位相同,则结果为0,否则为1 |
~ | 取反 | 对运算符翻转每一位,即0变成1,1变成0 |
<< | 左移 | 按位左移运算符。左操作数按位左移右操作数指定的位数。 |
>> | 右移 | 按位右移运算符。左操作数按位右移右操作数制定的位数。 |
>>> | 无符号右移 | 按位右移补零操作符。在>>之后,移动的得到的空位以零填充。 |
补充:无符号右移
10进制转二进制的时候,因为二进制数一般分8位、 16位、32位以及64位 表示一个十进制数,所以在转换过程中,最高位会补零。
计算机中负数使用二进制的补码表示。10进制转2进制的是源码,源码取反是反码,反码加1是补码。
A = 0011 1100
B = 0000 1101
------------------
A & B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~ A = 1100 0011
A << 2 = 1111 0000
A >> 2 = 1111
A >>> 2 = 0000 1111
------------------
C = 1111 1111 // (补码的-1)
C >>> 4 = 0000 1111
2. 常考技巧
2.1 基本表示
题目一:常用计算和位运算
- 取模运算转化成位运算 (在不产生溢出的情况下)
a % (2^n)
a & (2^n - 1)
- 乘法运算转化成位运算 (在不产生溢出的情况下)
a * (2^n)
a < < n
- 除法运算转化成位运算 (在不产生溢出的情况下)
a / (2^n)
a >> n
// 例: 12/8 == 12>>3
- 除以2的余数
a % 2
a & 1
- 相反数
(~x+1)
题目二:判断一个数 x 的奇偶性
n&1 == 1 ? "奇数" : "偶数"
如果 x 是奇数,则最低位是1,。注意是与,由于1的前面都是0,所以 x 的前面位无论是几,和0与都是0。
题目三:取int型变量 x 的第k位 (k=0,1,2……sizeof(int))
(x>>k) & 1
题目四:将int型变量a的第k位清0
x = x &~(1 << k)
题目五:整数的平均数
对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的。
int average(int x, int y) {
return (x&y)+((x^y)>>1);
}
题目六:计算绝对值
int abs( int x ) {
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}
2.2 去掉 x 的最后一个1
x = 1100
x-1 = 1011
x & (x-1) = 1000
题目一:用O(1)时间检测整数n是否是2的幂次.
思路:2的幂次满足两个条件
- n > 0
- n的二进制中只有一个1
用 x&(x-1) 将最后一个1去掉,如果是2的幂次,则结果为0.
题目二:计算在一个 32 位的整数的二进制表示中有多少个 1.
思路:
循环使用 x&(x-1) 去掉最后一个1,计算总共消去了多少次即可。
题目三:将整数A转换为B,需要改变多少个bit位.
思路:
如果A和B在第 i 个位上相等,则不需要改变这个bit位;如果在第 i 位上不相等,则需要改变这个bit位;所以问题转化为了A和B有多少个bit位不相同。联想到位运算有一个异或操作,相同为0,不同为1,所以问题转化为了计算A和B异或之后有多少个1.
2.3 使用二进制进行子集枚举
题目一:给定一个含不同整数的集合,返回其所有的子集
题目:如果 S = [1,2,3],有如下的解:[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2] ]
思路:
题目二:不用临时变量交换两个数
A = 5;
B = 12;
A = A^B;
B = B^A; // =B ^ (A^B) =A.
A = A^B; // =(A^B) ^ A =B.
参考
[1] Java位运算原理及使用讲解
[2] 位运算——强大得令人害怕