位运算

255 阅读2分钟

题目一

给定两个有符号32位整数a和b,返回a和b中较大的。【要求,不用做任何比较判断】

相关代码:

public class Code03_MaxHappy {
	//想法:c = a - b 如果得数是正返回a,得数是负返回b
	//因为不能用比较,所以可以依据c的最高位是1还是0进行判断
	public static int getMax1(int a, int b){
		int c = sign(a - b);
		return c == 0 ? a : b;
	}

	public static int sign(int n){
		return (n >> 31) & 1;
	}

	public static void main(String[] args) {
		System.out.println(getMax1(-2,2));
	}
}

但是上面a - b 可能会溢出,产生错误的答案,如 a = -2147483648, b = 2147483647

修正代码:

public class Code03_MaxHappy {
	//想法:c = a - b 如果得数是正返回a,得数是负返回b
	//因为不能用比较,所以可以依据c的最高位是1还是0进行判断
	public static int getMax1(int a, int b){
		int c = sign(a - b);
		int sa = sign(a);//a的符号
		int sb = sign(b);//b的符号
		//返回a的情况:同符号的情况下,差值c的最高位是0;或者不同符号的情况下,a的最高位是0
		return (c == 0 && sa == sb) || (sa != sb && sa == 0) ? a : b;
	}

	public static int sign(int n){
		return (n >> 31) & 1;
	}

	public static void main(String[] args) {
		System.out.println(getMax1(-2147483648,2147483647));
	}
}

题目二

判断一个32位正数是不是2的幂、4的幂

是不是2的幂相关代码:

class Solution {
    public boolean isPowerOfTwo(int n) {
        if(n <= 0){
            return false;
        }
        int r = n & (~ n + 1);//右边最近的1
        return r != n ? false : true;
    }
}
=========================================
class Solution {
    public boolean isPowerOfTwo(int n) {
        if(n <= 0){
            return false;
        }
        int r = n & (n - 1);//n - 1会将最右边的1给打散掉,与上原来的就是0
        return r != 0 ? false : true;
    }
}

是不是4的幂相关代码:

class Solution {
    public boolean isPowerOfFour(int n) {
        if(n < 0){
            return false;
        }
        int res = n & (n  - 1);//是4的幂就一定是2的幂。所以只有一个1
        //满足是2的幂后应该这个数 & 01010101 01010101 01010101 01010101 应该 != 0
        // return res == 0 && (n & 0b01010101010101010101010101010101) != 0 ? true : false;    
        return res == 0 && (n & 0x55555555) != 0 ? true : false; 
    }
}

题目三

给定两个有符号32位整数a和b,不能使用算术运算符,分别实现a和b的加、减、乘、除运算

要求:如果给定a、b执行加减乘除的运算结果就会导致数据的溢出,那么你实现的函数不必对此负责,除此之外请保证计算过程不发生溢出

加法

相关分析:不断地算无进位相加进位信息的结果,直到有一步没有了进位信息

image.png

class Solution {
    public int add(int a, int b) {
        int sum = a;
        while(b != 0){
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }
}

减法

class Solution {
    public int add(int a, int b) {
        //减法相当于相反数
        b = ~b + 1;
        int sum = a;
        while(b != 0){
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }
}

乘法

处理的方法和加法类似,乘法相当于多个加法

在B中遇到0,直接跳过,遇到1,就是重复A,但是要注意左移几位

image.png

class Solution {
    public int multiply(int A, int B) {
        int res = 0;
        while(B != 0){
            //遇到B中的1,停下来相加,没有直接跳过
            if((B & 1) != 0){
                res = add(res,A);
            }
            //A不断的左移一位
            A <<= 1;
            //方便辨认出B中相应位是0还是1
            B >>>= 1;
        }
        return res;
    }

    public int add(int A, int B){
        int sum = 0;
        while(B != 0){
            sum = A ^ B;
            B = (A & B) << 1;
            A = sum;
        }
        return sum;
    }
}

除法

image.png

class Solution {
    public int divide(int dividend, int divisor) {
        //因为除法的特殊性,所以对最大最小值进行单独的考虑,因为最小值是
        //-2147483648,而最大值是2147483647,这时候进行绝对值并且移动31位就会出错
        int ret = 0;
        //除数与被除数都是系统最小,结果返回1
        if(divisor == Integer.MIN_VALUE && dividend == Integer.MIN_VALUE){
            return 1;
        //除数是系统最小,结果返回0
        }else if(divisor == Integer.MIN_VALUE){
            return 0;
        //除数是-1,被除数是系统最小,结果返回2147483647,因为系统最大就是这个,没有2147483648
        }else if(divisor == -1 && dividend == Integer.MIN_VALUE){
            return Integer.MAX_VALUE;
        //上面条件过完时候,被除数是系统最小
        }else if(dividend == Integer.MIN_VALUE){
            //先让被除数加1,变成-2147483647进行计算,得到ret
            ret = divide((dividend + 1), divisor);
            //计算ret与除数相乘后与原数相差多少,相差的部分再除以除数,得到的结果加上ret
            //注意是原数 - (ret与除数相乘)
            return ret + divide((dividend - multi(ret, divisor)), divisor);    
        }
        int isFu = 1; 
        //相同是0,也就是返回正的
        if(((dividend >> 31) ^ (divisor >> 31)) == 0){
            isFu = 0;
        }
        //因为-7/2 和 7/-2是相同的答案,所以都转化成正数做,正负号做一些标记即可
        dividend = Math.abs(dividend);
        divisor = Math.abs(divisor);
        //除法的核心代码
        //这里的向右移动和图中的意思是等价的,以为担心符号位的问题,
        //所以向左移动会有风险,所以直接向右移动
        for(int i = 31; i > -1; i--){
            //找到不大于被除数的最大移动
            if((dividend >> i) >= divisor){
                //结果或上
                ret |= (1 << i);
                //被除数减去对应的,依次再进行
                dividend = dividend - (divisor << i);
            }
        }
        //依据标记的正负号进行最后的修饰
        return isFu == 0 ? ret : -1 * ret;
    }

    //位运算乘法
    public int multi(int a, int b){
        int res = 0;
        while(b != 0){
            if((b & 1) != 0){
                res += a;
            }
            a <<= 1;
            b >>>= 1;
        }
        return res;
    }
}