目录
燃烧吧!!!大脑!!!
位运算
运算符
逻辑运算符 * 位与&:两个十进制数转为二进制数从低到高按位运算,同位两个都为1,结果为1,最终将这个二进制数转为十进制得到结果, * 位或|:两个位都为0结果为0,否则为1 * 异或^:只有两个位不同为1,否则为0 ---> 无进位相加 * 按位取反:一元运算,当前数字转为二进制数,一变0,0变1 位移运算符 * 左移:二元运算符,x<<y,x左移y位,转换为二进制向左移动y位,末尾补0 * 右移:x>>y,转换为二进制,将所有位向右偏移y位,x为非负数则高位补0,x为负数则高位补1,右移一位可看做对x除2,并向下取整
位运算性质
性质
* 1、交换律可任意交换运算因子的位置,结果不变
* 2、结合律(即(a^b)^c==a^(b^c))
* 3、对于任何数x,都有x^x=0,x^0=x,同自己求异或为0,同0求异或为自己
* 4、自反性A^B^B=A^0=A,连续和同一个因子做异或运算,最终结果为自己
位运算理解(实现加减乘除)
二进制三十二位最高位表示正负。0正,1,负 负数为去掉符号位然后按位取反最后加一 位运算实现加减乘除(在Java虚拟机中用位运算拼凑) 1、加法==>a = a^b b = (a&b)<<1 * 异或:无进位相加 * 与:进位信息 * 直到进位信息消失,不进位信息就是结果 2、减法 * a加上b的相反数 * 相反数 ~b * add(a,add(~b,1)) 3、乘法 * 竖式相乘相加 * 当b= 0 时,直接左移右移,b=1时写a并左右移动 4、除法 * 将b移动到最接近a的时候,保证不比她大,然后给a减去这个数 * a:01101100 b:00001100->01100000 * 循环操作找到位置为q1的数
public static boolean isNeg(int n){
return n < 0;
}
public static int negNum(int n){
return add(~n,1);
}
//加法
public static int add(int a,int b){
int sum = a;
while(b!=0){
sum = a^b;//无进位相加
b = (a & b) << 1;//进位信息
a = sum;
}
return sum;
}
//减法
public static int minus(int a,int b){
return add(a,negNum(b));
}
//乘法
public static int multi(int a,int b){
int res = 0;
while(b != 0){
if ((b & 1) != 0){
res = add(res,a);
}
a <<= 1;
b >>>= 1;
}
return res;
}
//除运算
public static int divide(int a,int b){
if(a == Integer.MIN_VALUE && b ==Integer.MIN_VALUE){
return 1;
}else if(b == Integer.MIN_VALUE){
return 0;
}else if(a == Integer.MIN_VALUE){
if(b == negNum(1)){
return Integer.MAX_VALUE;
}else{
int c = div(add(a,1),b);
return add(c,div(minus(a,multi(c,b)),b));
}
}else{
return div(a,b);
}
}
public static int div(int a,int b){
int x = isNeg(a)?negNum(a):a;
int y = isNeg(b)?negNum(b):b;
int res = 0;
for (int i = 30; i >= 0; i--) {
if ((x >> i) >= y){
res |= (1<<i);
x = minus(x,y<<i);
}
}
return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
}
位运算操作
1.2的幂
n = 4 : (n&(n-1)) = 0 是2的幂
n | 1 | 0 | 0 |
n-1 | 0 | 1 | 1 |
n&(n-1) | 0 | 0 | 0 |
n = 5 :(n&(n-1)) != 0 不是2的幂
n | 1 | 0 | 1 |
n-1 | 1 | 0 | 0 |
n&(n-1) | 1 | 0 | 0 |
public static boolean stack2(int n){
if(n > 0 && (n & (n - 1)) == 0){
return true;
}
return false;
}
2.4的幂
与2的幂与4的幂相同,同时需要加上4的幂%3 = 1
public static boolean stack2(int n){
if(n > 0 && (n & (n - 1)) == 0 && n % 3 == 1){
return true;
}
return false;
}
3.不用额外变量两数交换(两个数在内存中是两个独立的地址)
假设 a = 4,b = 5
a | 1 | 0 | 0 |
b | 1 | 0 | 1 |
a = a^b | 0 | 0 | 1 |
b = a^b | 1 | 0 | 0 |
a = a^a | 1 | 0 | 1 |
public static void exch(int a,int b){
a = a^b;
b = a^b;
a = a^a;
}
4.位1的个数
与2的幂核心代码相同,每次n&(n-1)都会消除1个1
public static int times(int n){
int t = 0;
while(n != 0){
n &= (n-1);
t++;
}
return t;
}
5.打印某个数的二进制
public static void two(int num){
for(int i = 31;i >= 1;i--){
System.out.print((num & (i<<i)) == 0 ? "0":"1");
}
}
6.数组中只有一个数出现奇数次,求这个数;
int[] arr = new int[]{1,1,2,2,3,3,4,4,5};
int result = 0;
for(int i = 0;i < arr.length;i++){
result ^= arr[i];
}
System.out.println(result);
7.两种数(出现奇数次的数都为偶数或奇数时会出异常)出现了奇数次,其他数都为偶数次 ,求出现奇数次的两个数;
int[] arr = new int[]{1,1,2,2,3,3,4,4,5,6,6,6};
//result记录两个出现奇数次的数异或的结果
int result = 0;
for(int i = 0;i < arr.length;i++){
result ^= arr[i];
}
//两个出现了奇数次的数某一位不同时,result的对应那一位就为1,可以根据这个1进行求解
int temp = result & (~result + 1);//求最右边的1,也可以是0
int result1 = 0;
for(int cur : arr){
if((cur & temp) == 1){
result1 ^= cur;
}
}
System.out.println(result1 + " " + (result ^ result1));