位运算的题目小结

位运算的基础

位操作都是对补码进行操作
正数的补码是自己
负数的补码是源码取反+1
 
已知补码求反码:补码-1后取反,取反过程中符号位不变
补码:源码取反+1,取反过程中符号位不变
 
两次异或之后还是原值
a^b^b = a 可以应用在加密上 b是密钥
 
异或:实现两个变量值的交换
a = a ^ b
b = a ^ b 【a^b^b = a】
a = b ^ a 【a^b^a = b】
异或是满足交换律的
 
<<左移 操作数 * 2
>>右移 操作数 / 2
>>>无符号右移 操作数/2
区别:右移操作后 左边有位置空出来。 空位置用操作数的符号位置的数补全
无符号右移 左边空出来的位置用0补全

不使用比较判断找出较大值

 1     //参数n不是0 就是1
 2     // 1>0 0>1 实现1变0,0变1
 3     public static int flip(int n){
 4         return n ^ 1;
 5     }
 6     //n是负数 返回0
 7     //n是非负数 返回1
 8     public static int sign(int n){
 9         return flip((n >> 31) & 1);
10         //n>>31是把符号位移到最后,
11         // 如果是负数 ******1&00000001 = 1 -> flip -> 0
12         // 如果是正数 ******0&00000001 = 0 -> flip -> 1
13     }
14     
15     public static int getMax(int a, int b){
16         int c = a-b;
17         int signA = sign(c);//如果a>=b signC = 1; a<b signC = 0
18         int signB = flip(signA);//signA反转
19         return signA * a + signB * b;
20     }

上述代码存在问题:a-b可能会溢出

 1     public static int getMax2(int a, int b){
 2         int sa = sign(a);
 3         int sb = sign(b);
 4         int sc = sign(a-b);
 5         int difSab = sa ^ sb; //ab符号是否一样  一样 0 不一样 1
 6         int sameSab = flip(difSab);// 一样 1 不一样 0
 7         //如果a和b的符号相同 肯定不会溢出
 8         
 9         //在a和b符号相同的情况下 a-b>=0; 返回A【sameSab * sc】
10         //在a和b符号不同的情况下可能溢出,但如果A大于0,可直接返回【difSab * sa】
11         int returnA = sameSab * sc + difSab * sa;
12         //A B互斥 直接反转
13         int returnB = flip(returnA);
14         return a * returnA + b * returnB;
15         
16     }

上述代码解决了溢出问题。return的部分保证returnA和returnB互斥,只会返回a或b

判断32位正数是不是2的幂,4的幂

2的幂:2进制状态中只有1个1【充要条件】

获取最右侧的1:x&(-x)

【二进制中最右边的 1,且其它位全部为 0。】

x & (-x) == x 那么x就是2的幂

去除最右侧的1:x & (x - 1)

【最右边的 1 设置为 0】

(x - 1)代表了将 x 最右边的 1 设置为 0,并且将较低位设置为 1。

如果 x & (x - 1) == 0 那么 x就是2的幂【因为它只有1位是1】

4的幂:4的幂一定是2的幂,所以4的幂也只有1个1【必要不充分条件】

4的幂的1一定在 1位、3位、5位、7位....上(奇数位)

32位 0x55555555 = 0101010101....0101(奇数位上全是1)

所以只要x & 010101.....010101 != 0  则是4的幂

1     public static boolean isTowPower(int x){
2         return (x & -x) == x;//取出1
3         //return (x & (x-1)) == 0;//去掉1
4     }
5     public static boolean isFourPower(int x){
6         return isTowPower(x) && (x & 0x55555555)!=0;
7     }

不使用算数运算符实现a和b的加减乘除

加法

前提:a和b运算后的结果一定不会溢出。

异或:无进位相加(不包含进位信息的加法结果)

进位信息:&之后左移1位

12 + 6

01100

00110

^--------

01010

&-------

00100

<<------

01000

异或结果+进位信息 直到不再产生进位信息

01010

01000

^-------

00010

&------

01000

<<-----

10000

异或结果+进位信息 直到不再产生进位信息

00010

10000

^-------

10010

&------

00000

得到结果:10010 = 2+16 = 18

1     public static int add(int a, int b){
2         int sum = a;
3         while(b!=0){
4             sum = a ^ b;//无进位相加
5             b = (a & b) << 1;//进位信息
6             a = sum;//无进位相加+进位信息 直到 进位信息==0
7         }
8         return sum;
9     }

减法

a - b = a + (-b)

相反数:一个数取反+1,就是这个数的相反数(求负数补码的方法)

    public static int sub(int a,int b){
        return add(a,add(~b,1));
    }

乘法

被乘数 X 乘数 = 

对于乘数的每一位,如果是 0 :00000000左移1位

如果是1:被乘数左移1位(在上一次的基础上左移)

+--------------------------------------------------------------------

得到结果

00001000

00000110

------

00010000

00000011

------

add (00010000+0=00010000)

00100000

00000001

add (00100000+00010000=00110000)

------

01000000

00000000

返回00110000=32+16 = 48 √

    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;//无符号右移用0补位
        }
        return res;
    }

除法

猜你喜欢

转载自www.cnblogs.com/xdcat/p/12984786.html
今日推荐