根本的位操作符有与、或、异或、取反、左移、右移这 6 种:
位运算示例操作
位运算示例操作
位运算 | 性能 | 示例 | |
---|---|---|---|
x >> 1 | 去掉最初一位 | 101101->10110 | |
x << 1 | 在最初加一个 0 | 101101->1011010 | |
x << 1 | 在最初加一个 1 | 101101->1011011 | |
x\ | 1 | 把最初一位变成 1 | 101100->101101 |
x & -2 | 把最初一位变成 0 | 101101->101100 | |
x ^ 1 | 最初一位取反 | 101101->101100 | |
x \ | (1 << (k-1)) | 把右数第 k 位变成 1 | 101001->101101,k=3 |
x & ~ (1 << (k-1)) | 把右数第 k 位变成 0 | 101101->101001,k=3 | |
x ^(1 <<(k-1)) | 右数第 k 位取反 | 101001->101101,k=3 | |
x & 7 | 取末三位 | 1101101->101 | |
x & (1 << k-1) | 取末 k 位 | 1101101->1101,k=5 | |
x >> (k-1) & 1 | 取右数第 k 位 | 1101101->1,k=4 | |
x \ | ((1 << k)-1) | 把末 k 位变成 1 | 101001->101111,k=4 |
x ^ (1 << k-1) | 末 k 位取反 | 101001->100110,k=4 | |
x & (x+1) | 把左边间断的 1 变成 0 | 100101111->100100000 | |
x \ | (x+1) | 把右起第一个 0 变成 1 | 100101111->100111111 |
x \ | (x-1) | 把左边间断的 0 变成 1 | 11011000->11011111 |
(x ^ (x+1)) >> 1 | 取左边间断的 1 | 100101111->1111 | |
x & -x | 去掉右起第一个 1 的右边 | 100101000->1000 | |
x&0x7F | 取末 7 位 | 100101000->101000 | |
x& ~0x7F | 是否小于 127 | 001111111 & ~0x7F->0 | |
x & 1 | 判断奇偶 | 00000111&1->1 |
应用位运算的两点注意事项:
- 位操作只能用于整形数据,对 float 和 double 类型进行位操作会被编译器报错。
- 位操作符的运算优先级比拟低,因为尽量应用括号来确保运算程序
1. 判断一个数值是不是 2 的整数次方
解题思路:
2 的整数次方对应的二进制的最高位上只有一个 1,如:8,二进制为 1000;4,二进制为 0100,
那么将该数字减去 1 再与该数字进行与运算,减去 1 后失去二进制:7,二进制为 0111;3,二进制为 0011,能够看出 8&7 为 0,
4&3 为 0
所以,如果 n 是 2 的整数次方,那么 n & (n – 1) 后果肯定为 0:
n 的数值要大于 0
1. public class Main {3. public static void main(String[] args) {
4. int n = 8;
5. if ((n & (n-1)) == 0){6. System.out.println("整数的二次方 true");
7. }else{8. System.out.println("不是整数的二次方");
9. }
10. }
11. }
2. 应用位运算替换两个数字【不应用两头变量】
应用异或
1. public class Main {3. public static void main(String[] args) {
4. int n = 8, m = 10;
5. n ^= m;
6. m ^= n;
7. n ^= m;
8. System.out.println(n + "," + m);
9. }
10. }
如:a = 13, b = 6:
a 的二进制为 13 = 8 + 4 + 1 = 1101(二进制)
b 的二进制为 6 = 4 + 2 = 110(二进制)
- a ^= b a = 1101 ^ 110 = 1011;
- b ^= a b = 110 ^ 1011 = 1101; 即 b == 13
- a ^= b a = 1011 ^ 1101 = 110; 即 a == 6
其余办法, 应用加减法
1. public class Main {3. public static void main(String[] args) {
4. int n = 8, m = 10;
5. n = n + m;
6. m = n - m;
7. n = n - m;
8. System.out.println(n + "," + m);
9. }
10. }
3. 计算在一个 32 位的整数的二进制示意中有多少个 1
循环应用 x & (x-1) 消去最初一位 1,计算总共消去了多少次即可。
如:
13:1101
12:1100
相与:1100,消去最初一位
1. public class Main {3. public static void main(String[] args) {
4. // 计算在一个 32 位的整数的二进制示意中有多少个 1
5. int m = 13, num = 0;
6. while (true){7. if (m == 0) break;
8. m &= (m-1);
9. num ++;
10. }
11. System.out.println(num);
12. }
14. }
4. 负数变成正数,或者正数变成负数
变换符号只须要取反后加 1 即可
1. public class Main {3. public static void main(String[] args) {
4. // 计算在一个 32 位的整数的二进制示意中有多少个 1
5. int m = -13;
6. int changeM = ~m + 1;
7. System.out.println(changeM);
8. }
10. }
5. 判断一个数值的奇偶
只有依据最未位是 0 还是 1 来决定,为 0 就是偶数,为 1 就是奇数,所以只须要与 1 相与。
因而能够用 if ((a & 1) == 0) 代替 if (a % 2 == 0) 来判断 a 是不是偶数。
1. public class Main {3. public static void main(String[] args) {
4. int m = -14;
6. if ((m & 1) == 1){7. System.out.println("ji");
8. }else{9. System.out.println("ou");
10. }
11. }
13. }
6. 乘以 2 的 m 次方操作
乘以 2 的操作,即 2 的 1 次方,左移 1 位
System.out.println(10<<1);
推导扩大:
乘以 2 的 m 次方
System.out.println(10<<2); // 乘以 2 的 2 次方,相当于乘以 4
7. 除以 2 运算 (负奇数的运算不可用)
System.out.println(10>>1);
8. 转换成绝对值
1. public class Main {3. public static void main(String[] args) {
4. int n = 12;
6. System.out.println(0 >> 31); // 0
7. System.out.println(10 >> 31); // 0
8. System.out.println(-10 >> 31); // -1
10. System.out.println((n ^ (n >> 31)) - (n >> 31)); // 12
12. }
14. }
- 首先:n>>31 获得 n 的符号
若 n 为负数,n>>31 等于 0;若 n 为正数,n>>31 等于 -1 若 n 为负数 n^0- 0 数不变;
- 若 n 为正数 n^-1 须要计算 n 和 -1 的补码,异或后再取补码,后果 n 变号并且绝对值减 1,再减去 - 1 就是绝对值
9. 判断两数符号是否雷同
true 示意 x 和 y 有雷同的符号,false 示意 x,y 有相同的符号。
System.out.println((a ^ b) > 0);
10. 求两个整数(int)的平均数
System.out.println((a+b) >> 1);
11. 求两个整数的最大值
1. int max(int a,int b){2. return b & ((a-b) >> 31) | a & (~(a-b) >> 31);
3. /* 如果 a >=b,(a-b)>>31 为 0,否则为 -1*/
4. }
12. 求两个整数的最小值
1. int min(int a,int b){2. return a & ((a-b) >> 31) | b & (~(a-b) >> 31);
3. /* 如果 a >=b,(a-b)>>31 为 0,否则为 -1*/
4. }
13. 两个整数的加法运算
应用 ^
和 &
将两个整数相加
- 两个数异或:相当于两个数相加,而不思考进位;
- 两个数相与,并左移一位:相当于求得进位;
13+11 =?;
13 的二进制 1 1 0 1 —–a 13
11 的二进制 1 0 1 1 —–b 11
(a&b) <<1 -> 1 0 0 1 0 —–d 18
a^b -> 0 1 1 0 —–e 6
(d&e) <<1 -> 0 0 1 0 0 ——f 4
d^e -> 1 0 1 0 0 —–g 20
(f&g) <<1 -> 0 1 0 0 0 ——h 8
f^g -> 1 0 0 0 0 ——i 16
(h&i) <<1 -> 0 0 0 0 0 ——h 0 —- ——– 没有进位了,则退出循环
h^i -> 1 1 0 0 0 ——i 24
1. private static int getSum(int a, int b) {2. if (a == 0) return b;
3. if (b == 0) return a;
4. while (b != 0) {
5. int carry = a & b; // 失去有进位的地位
6. a = a ^ b; // 间接相加,然而没有进位
7. b = carry << 1; // 失去进位
8. }
9. return a;
10. }
我的项目举荐:
2000 多 G 的计算机各行业电子资源分享(继续更新)
2020 年微信小程序全栈我的项目之喵喵交友【附课件和源码】
Spring Boot 开发小而美的集体博客【附课件和源码】
Java 微服务实战 296 集大型视频 - 谷粒商城【附代码和课件】
Java 开发微服务畅购商城实战【全 357 集大我的项目】- 附代码和课件
最全最具体数据结构与算法视频 -【附课件和源码】