原理
1. 基本原理
0s 表示一串 0,1s 表示一串 1。
x ^ 0s = x x & 0s = 0 x | 0s = x
x ^ 1s = ~x x & 1s = x x | 1s = 1s
x ^ x = 0 x & x = x x | x = x
- 利用 x ^ 1s = ~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。
- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。
位与运算技巧:
- n&(n-1) 去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110100,减去 1 得到 10110011,这两个数相与得到 10110000。
- n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。
- n-n&(~n+1) 去除 n 的位级表示中最高的那一位。
移位运算:
- >> n 为算术右移,相当于除以 2n; 有符号左右移动
- >>> n 为无符号右移,左边会补上 0。
- << n 为算术左移,相当于乘以 2n。
一、无符号右移“>>”与有符号右移“>>>”
Java提供了两种右移运算符:“>>” 和">>>"。其中,“>>”被称为有符号右移运算符,“>>>”被称为无符号右移运算符,它们的功能是将参与运算的对象对应的二进制数右移指定的位数。二者的不同点在于“>>”在执行右移操作时,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。而“>>>”则不同,无论参与运算的数字为正数或为负数,在执运算时,都会在高位补0。
此外,需要注意的是,在对char、byte、short等类型的数进行移位操作前,编译器都会自动地将数值转化为int类型,然后才进行移位操作。由于int型变量只占4Byte(32bit),因此当右移地位数超过32bit时,移位运算没有任何意义。所以,在Java语言中,为了保证移动位数地有效性,以使右移的位数不超过32bit,采取了取余的操作,即a>>n等价于a>>(n%32)。
二、“<<”运算符与“>>”运算符
"<<"运算符表示左移,左移n位表示原来的值乘2的n次方,经常用来代替乘法操作。例如,一个数m乘以16可以表示为将这个数左移4位(m<<4),由于CPU直接支持位运算,因此位运算比乘法运算效率高。
与右移运算不同的是,左移运算没有有符号和无符号左移动,在左移时,移除高位的同时在低位补0。 不存在无符号左移
与右移运算相同的是,当进行左移运算时,如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模,例如对int类型的数移动33位,实际上只移动了33%32=1位。
2. mask 计算
要获取 111111111,将 0 取反即可,~0。
要得到只有第 i 位为 1 的 mask,将 1 向左移动 i-1 位即可,1<<(i-1) 。例如 1<<4 得到只有第 5 位为 1 的 mask :00010000。
要得到 1 到 i 位为 1 的 mask,1<<(i+1)-1 即可,例如将 1<<(4+1)-1 = 00010000-1 = 00001111。
要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 ~(1<<(i+1)-1)。
3. Java 中的位操作
static int Integer.bitCount(); // 统计 1 的数量
static int Integer.highestOneBit(); // 获得最高位
static String toBinaryString(int i); // 转换为二进制表示的字符串
[LeetCode] Hamming Distance 汉明距离
The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
Given two integers x
and y
, calculate the Hamming distance.
Note:
0 ≤ x
, y
< 231.
Example:
Input: x = 1, y = 4
Output: 2
Explanation:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
The above arrows point to positions where the corresponding bits are different.
统计两个数的二进制表示有多少位不同
Input: x = 1, y = 4
Output: 2
Explanation:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
The above arrows point to positions where the corresponding bits are different.
class Solution {
public int hammingDistance(int x, int y) {
int z = x ^ y;
int count = 0;
while(z != 0){
if(z % 2 == 1)
count++;
z /= 2; //最后异或的结果都当成正整数来考虑了
}
return count;
}
}
对两个数进行异或操作,位级表示不同的那一位为 1,统计有多少个 1 即可。
public int hammingDistance(int x, int y) {
int z = x ^ y;
int cnt = 0;
while(z != 0) {
if ((z & 1) == 1) cnt++;
z = z >> 1;
}
return cnt;
}
使用 z&(z-1) 去除 z 位级表示最低的那一位。
public int hammingDistance(int x, int y) {
int z = x ^ y;
int cnt = 0;
while (z != 0) {
z &= (z - 1);// 比如z= 100001 怎么与得到的最后一位都是0
//直到最后为0说明与的次数够了到头了
cnt++;
}
return cnt;
}
可以使用 Integer.bitcount() 来统计 1 个的个数。
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}