关于算法题目中有关位运算的方法总结,转自LeetCode评论 A summary: how to use bit manipulation to solve problems easily and efficiently
以下来自 Google 翻译
位操作是通过算术操作位或其他短于数据的数据段的操作。需要位操作的计算机编程任务包括低级设备控制,错误检测和纠正算法,数据压缩,加密算法和优化。 对于大多数其他任务,现代编程语言允许程序员直接使用抽象而不是代表抽象的位。 进行位操作的源代码使用按位操作:AND,OR,XOR,NOT和位移。
在一些情况下,位操作可以避免或减少循环遍历数据结构的需要,并且可以提供多倍的加速,因为位操作是并行处理的,但代码可能变得更难以编写和维护。
位运算的核心是位操作符 &
(and), |
(or), ~
(not) 和 ^
(XOR) 和移位运算符, a << b
和 a >> b
.
java中的移位运算符有三种, >>>
使用 0
填充高位; >>
使用符号位填充高位; <<
左移操作。
操作 | 英文 | 举例 |
---|---|---|
取并集 | Set Union | A | B |
取交集 | Set intersection | A & B |
取A中不属于B的部分 | Set subtraction | A & ~B |
按位取反 | Set negation | ALL_BITS ^ A or ~A |
将某位设置为1 | Set bit | A |= 1 << bit |
将某位设置为0 | Clear bit | A &= ~(1 << bit) |
测试某位的值 | Test bit | (A & 1 << bit) != 0 |
提取最后一个1 | Extract last bit | A & -A or A & ~(A - 1) or x ^ (x & (x - 1)) |
删除最后一个1 | Remove last bit | A & (A - 1) |
获取全部为1的字节 | Get all 1-bits | ~0 |
计算给定数字二进制表示中1的个数
private static int countOne(int n) {
int count = 0;
while (n != 0) {
n = n & (n - 1);
count++;
}
return count;
}
判断是否是4的幂数(可以使用映射,迭代或者递归方法)
private static boolean isPowerOfFour(int n){
return (n & (n - 1)) == 0 && (n & 0x55555555) != 0;
}
首先,如果一个数字是4的幂数,那么它的二进制肯定只有一个1,也就是 (n&(n-1))==0
。
然后在看0x55555555
,它的二进制表示是: 0101 0101 0101 0101 0101 0101 0101 0101
也就是只有都是4的倍数的值拼凑起来的数字和它进行 &
运算才能得到不是0的结果。
所以一个数字,是4的倍数,并且它的二进制表示中只有一个 1,那么它是4的幂数。同理可以求8的幂数等。
^
技巧使用 ^
来删除二进制表示中数字完全相同的位从而保存不同的,或者保存不同的位删除相同的。
计算两个整数的加法运算
使用 ^
和 &
将两个整数相加
private static int getSum(int a, int b) {
if (a == 0) return b;
if (b == 0) return a;
while (b != 0) {
int carry = a & b; // 得到有进位的位置
a = a ^ b; // 直接相加,但是没有进位
b = carry << 1; // 得到进位
}
return a;
}
找到缺少的数字
给出一个数组,这个数组都是由不同的数字组成的,这些数字的取值从 0 到 n,请找出那个丢失的数字。
private static int missingNumber(int[] nums) {
int ret = 0;
for (int i = 0; i < nums.length; i++) {
ret ^= i;
ret ^= nums[i];
}
return ret ^ nums.length; // 数组中少了一个数,所以这里要加上
}
|
技巧尽量包容下所有的值为 1 的位。
给定一个数字 N,找出最大的 2 的幂数小于或者等于 N 的值。
private static long largestPower(long N) {
//changing all right side bits to 1.
N = N | (N >> 1);
N = N | (N >> 2);
N = N | (N >> 4);
N = N | (N >> 8);
N = N | (N >> 16); // java中要是int型,只需要到这步
N = N | (N >> 32);
return (N + 1) >> 1;
}
将当前数字的二进制表达式中的最高位的 1 右边的所有的数字全部置为 1。然后呢,我们再处理最高位的问题,怎么处理这个最高位呢?我们可以使之加 1,实现进位加 1,然后我们再右移 1 位,保证当前数字小于等于参数值。
反转给出的32位无符号整数
java 中没有无符号整数,下面给出c代码:
uint32_t reverseBits(uint32_t n) {
unsigned int mask = 1<<31, res = 0;
for(int i = 0; i < 32; ++i) {
if(n & 1) res |= mask; //从右往左
mask >>= 1; // 从左往右
n >>= 1;
}
return res;
}
uint32_t reverseBits(uint32_t n) {
uint32_t mask = 1, ret = 0;
for(int i = 0; i < 32; ++i){
ret <<= 1; // 低位向高位移动
if(mask & n) ret |= 1; // 判断低位上的数字
mask <<= 1; // 从低位到高位进行扫描
}
return ret;
}
&
技巧用来选择一些需要的位
反转一些指定的位
x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1);
x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2);
x = ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4);
x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
x = ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
十六进制 | 二进制 | 功能 |
---|---|---|
0xaa | 1010 1010 | 选择指定 1 位 |
0x55 | 0101 0101 | 与0xaa的位置刚好相反 |
0xcc | 1100 1100 | 选择指定 2 位 |
0x33 | 0011 0011 | 与0xcc的位置刚好相反 |
0xf0 | 1111 0000 | 选择指定 4 位 |
0x0f | 0000 1111 | 与0x0f的位置刚好相反 |
给定一个范围[m, n],其中 0 <= m <= n <= 2147483647(0x7FFFFFFF),返回指定范围内所有数字的相与结果。
比如给出 [5,7]
,结果为4
private static int rangeBitwiseAnd(int m, int n) {
int a = 0;
while (m != n) { // 找到高位相同的地方
m >>= 1;
n >>= 1;
a++;
}
return m << a; // &运算,低位不同的,都变成0了
}
后续会把LeetCode里面相关的题目链接总结在这里,持续更新中
Easy - 136. Single Number
Easy - 190. Reverse Bits
Easy - 191. Number of 1 Bits
Easy - 231. Power of Two
Easy - 268. Missing Number
Easy - 342. Power of Four
Easy - 371. Sum of Two Integers
A summary: how to use bit manipulation to solve problems easily and efficiently
(翻译整理)如何简单、高效地使用位操作解决问题