位运算是针对二进制的每一位进行的运算,它是专门针对数字0和1进行的操作。程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算是直接对整数在内存中的二进制位进行操作码,位运算即可以节约内存,同时使程序速度更快效率更高。
Java位用算运算符又可以分为 逻辑运算符 和 位移运算符 ;
逻辑运算符有:按位与 &、按位或 |、取反 ~、按位异或^;
位移运算符有:左移 <<、右移 >>、无符号右移 >>>;
在所有的位运算符中,除~以外,其余均为二元运算符操作数,并且位运算的运算对象为整型和字符型数据
运算规则:针对二进制,相同的为0,不同的为1
public static void main(String[] args) {
System.out.println("3^4运算的结果是 :"+(3^4));
//3 =======>011
//4 =======>100
//异或结果为:7(111)
}
特点:
1.任何一个数字异或他自己都等于0;
2.任何一个数字与0异或都是它自己;
3.a ^ b就是a和b相加之后,该进位的地方不进位的结果;
4.与指定位全是1进行^ 异或运算,可实现该位反转(1变0,0变1);
运算规则:两数相与,只要有一个为0,就为0,同时为1才为1.
public static void main(String[] args) {
System.out.println("3&4运算的结果是 :"+(3&4));
//3 =======>011
//4 =======>100
//与结果为:0(000)
}
特点:
1.与对应数据位全1的数&与运算,可取得该位本身的值;
2. 与对应数据位全0的数&与运算,可将该位置0。
运算规则:两数相或,只要有一个为1,就为1,同时为0才为0.
public static void main(String[] args) {
System.out.println("3|4运算的结果是 :"+(3|4));
//3 =======>011
//4 =======>100
//或结果为:7(111)
}
特点:
1.与对应数据位全1的数|与运算,则将该位置1
运算规则:取反运算是只针对一个数据进行操作,如果二进制是0,则取反为1,如果二进制是1,则取反为0;
public static void main(String[] args) {
System.out.println("~3运算的结果是 :"+(~3));
//3 =======>11
//非结果为:0(00)
}
运算规则:针对二进制,转换成二进制后向左移动,低位用0补齐,高位溢出则丢弃,遇到符号位,符号位保持不变,不参与运算
public static void main(String[] args) {
System.out.println("2<<3运算的结果是 :"+(2<<3));
//打印的结果是: 2左移3位的运算的结果是 :16
}
特点:1.左移1位相当于*2。左移几位相当于乘几个2.
运算规则:针对二进制,转换成二进制后向右移动,遇到符号位,符号位保持不变,不参与运算,低位溢出并舍弃,并用符号位补丢失的高位[即负数补1,正数补0];
public static void main(String[] args) {
System.out.println("4>>2运算的结果是 :"+(4>>2));
//打印的结果是: 4右移两位的运算的结果是 :1
}
特点:1.右移1位相当于/2。右移几位相当于/几个2.
运算规则:无符号右移运算是将操作数所有二进制值逐位右移若干位,包括最高位符号位,也跟着右移,低位溢出并舍弃,高位补0;
注意,无符号右移(>>>)中的符号位(最高位)也跟着变;
public static void main(String[] args) {
System.out.println("4>>>2运算的结果是 :"+(4>>2));
//打印的结果是: 4无符号右移两位的运算的结果是 :1
}
例如
00000100>>>2 结果00000001
00000111>>>2 结果00000001
10000100>>>2 结果00100001
10000111>>>2 结果00100001
编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数。(leetcode191)
观察一下 n 与 n-1 这两个数的二进制表示:对于 n-1 这个数的二进制来说,相对于 n 的二进制,它的最末位的一个 1 会变成 0,最末位一个 1 之后的 0 会全部变成 1,其它位相同不变。
比如 n = 8888,其二进制为 10001010111000
则 n - 1 = 8887 ,其二进制为 10001010110111
通过按位与操作后:n & (n-1) = 10001010110000
也就是说:通过 n&(n-1)这个操作,可以起到消除最后一个1的作用。
所以可以通过执行 n&(n-1) 操作来消除 n 末尾的 1 ,消除了多少次,就说明有多少个 1 。
public static int findOne(int n)
{
int countOne = 0;
while (n > 0){
count++;
n = (n - 1) & n;
}
return countOne;
}
给定一个整数,编写一个函数来判断它是否是 2 的幂次方(LeetCode231)。
public static boolean isPowOfTwo(int n) {
int CountOne = 0;
while(n > 0) {
CountOne += (n & 1);
n >>= 1;
}
return CountOne == 1;
}
public static boolean isPowOfTwo(int n) {
return ((n&(n-1))==0);
}
给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)(LeetCode201 )。
输入: [5,7]
输出: 4
输入: [0,1]
输出: 0
由于是按位与,那么某位一旦出现0,结果该位肯定是0。所以只需要考虑m,n都是1的位置。那么直接从高位开始,往低位走,直到遇到该位对应的数字不相等,将其后及其以后的数为都置为0,即为[m,n]之间所有的数字按位与的结果。
1.那么则先让俩数向右移位,然后判断两数是否相等,并记录移动的位数,当移动至两数相等时,则找到了两数左边的公共部分,则再让该数末尾补零至与原来的数长度相同即可。
public static int solution(int m , int n) {
int i = 0;
while(m != n ) {
m = m>>1;
n = n>>1;
i++;
}
return (m<<i);
}
2.也可以先建立一个 32 位都是 1 的最大数,然后每次向左移一位,比较 m 和 n 是否相同,不同再继续左移一位,直至相同,然后把 m 和 int_max相与就是最终结果。
public static int solution2(int m,int n) {
int int_max = Integer.MAX_VALUE ;//2147483647
while ((m & int_max) != (n & int_max)) {
int_max <<= 1;
}
return m & int_max;
}
颠倒给定的 32 位无符号整数的二进制位。
示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
对于这道题,我们只需要把要翻转的数从右向左一位位的取出来,如果取出来的是1,我们将结果res左移一位并且加上1;如果取出来的是0,我们将结果res左移一位,然后将要翻转的数n右移一位即可,代码如下
public static int reverseBit(int n) {
int res = 0;
for (int i = 0; i < 32; ++i) {
if ((n & 1) == 1) {
res = (res << 1) + 1;
} else {
res = res << 1;
}
n = n >> 1;
}
return res;
}