给定两个32位整数a
和b
,返回a
和b
较大的数,要求不做任何比较
/**
* 如果n为1返回0,如果为0,返回1
* @param n
* @return
*/
public static int flip(int n) {
return n ^ 1;
}
/**
* 返回整数n的符号,正数和0返回1,负数返回0
* @param n
* @return
*/
public static int sign(int n) {
return flip((n >> 31) & 1);
}
/**
* 返回两个数中最大的数
* @param a
* @param b
* @return
*/
public static int getMax(int a, int b) {
int c = a - b;
int sa = sign(a);
int sb = sign(b);
int sc = sign(c);
//difSab=1 sameSab=0 表示两个数符号不同
//difSab=0 sameSab=1 表示两个数符号相同
int difSab = sa ^ sb;
int sameSab = flip(difSab);
int returnA = difSab * sa + sameSab * sc;
int returnB = flip(returnA);
return a * returnA + b * returnB;
}
因此,
如果a符号和b符号不同(difSab=1 sameSab=0 表示两个数符号不同)
如果a符号与b符号相同(difSab=0 sameSab=1 表示两个数符号相同),a-b绝对值不会溢出
给定两个32位整数a
和b
,可正,可负,可0,不使用算术运算,实现a
和b
的加减乘除运算,但给定的a
和b
执行加减乘除的某些结果本来就会导致数据溢出,可以不必理会
因为第i位上只有1与1相加才产生i-1进位
/**
* 位运算实现加法
* @param a
* @param b
* @return
*/
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;
}
位运算实现减法,只要实现a+(-b)
即可,得到一个相反数就是数的二进制取反+1
/**
* 求相反数
* @param n
* @return
*/
public static int negNum(int n) {
return add(~n, 1);
}
/**
* 位运算求减法
* @param a
* @param b
* @return
*/
public static int minus(int a, int b) {
return add(a, negNum(b));
}
位运算实现乘法。
a x b = a x 2 0 x b 0 + a x 2 1 x b 1 + . . . + a x 2 i x b i + . . . + a x 2 31 x b 31 axb=ax2^0 xb_0 + ax2^1 xb_1 +...+ax2^ixb_i + ... + ax2^{31} xb_{31} axb=ax20xb0+ax21xb1+...+ax2ixbi+...+ax231xb31,其中bi是0或1代表整数b的二进制数表达中第i
位的值
/**
* 位运算求乘法
* @param a
* @param b
* @return
*/
public static int multi(int a, int b) {
int res = 0;
while (b != 0) {
if ((b & 1) != 0) {
res = add(res, a);
}
//a左移1位
a <<= 1;
//b右移1位
b >>>= 1;
}
return res;
}
只适合a、b不是负数先找到a能包含的最大部分,然后让a减去这个最大部分,再让剩下的a找到次大部分,并依次找下去,如果a和b有一个为负数或者都为负数时,可以先把a和b转正数,计算完成后看res的真实符号
a、b都不为负数,假设a=286,b=22,res=0
b左移31位、30位、…、4位时得到的结果大于a,说明a包含不下bx232bx2^4^任何一个,所以res4res31这些位置都是0,当b左移3位时结果位010110000,此时a≥b,说明a可以包含一个bx23,即res3=1,
剩下的a,即a-bx23
b<<2为001011000,此时a≥b,说明剩下a可以包含一个bx22,即res2=1,
剩下的a,即a-bx23-bx22
b<<1后大于a,说明剩下的a不能包含bx21
b<<0后a==b,说明剩下的a能包含一个bx20,即res0=1
当剩下的a再减去一个b后,结果为0,说明a被b除净,结果就是此时的res,即000001101=13
上述只能适用于a和b都不是负数,a和b两个都是负数或者有一个负数可以先转成正数,再求解
/**
* 判断是否为负数
* @param n
* @return
*/
public static boolean isNeg(int n) {
return n < 0;
}
/**
* 无法处理负数最大值的情况
* @param a
* @param b
* @return
*/
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 = 31; i > -1; i = minus(i, 1)) {
if ((x >> i) >= y) {
res |= (1 << i);
x = minus(x, y << i);
}
}
return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
}
最关键一步
32位整数最小值为-2147483648,最大值为2147483647,最小值的绝对值比最大值的绝对值大1
所以,a或b等于最小值是转换不成相应正数的
假设整数的最大值为9,最小值为-10
/**
* 位运算求除法
* @param a
* @param b
* @return
*/
public static int divide(int a, int b) {
if (b == 0) {
throw new RuntimeException("divisor is 0");
}
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) {
//res=(a+1)/b
int res = div(add(a, 1), b);
//res+(a-res*b)/b
return add(res, div(minus(a, multi(res, b)), b));
} else {
return div(a, b);
}
}