将两个整数相除,要求不使用乘法、除法和 mod 运算符。如果溢出(超出32位有符号整型表示范围),返回 Integer.MAX_VALUE。
Input:a = 10, b= 3
Output: 3
问题分析:
这题让计算两个数的商,但不能使用乘法和除法。我们可以考虑使用减法,用被除数不断的减去除数,直到不够减为止。如果每次只减去一个除数,这样效率明显很差,我们可以考虑减去除数的倍数。假如这两个数的商是 X ,这个 X 在二进制中有些位置是 0 有些位置是 1 。我们每次只需要减去位置是 1 的数字和除数的乘积即可。
因为被除数和除数可能符号不一致,我们这里统一把它们都转换为负数,然后在相减。这里能不能都转为正数呢,实际上是不行的,因为在 int 范围内,负数的范围要比正数的范围大[-2147483648,2147483647],如果负数是 -2147483648 ,把它转为正数会出现溢出。
这里关键点是怎么确定减去除数的多少倍?我们只需要尝试把除数往左移一位,判断是否大于被除数(这里被除数和除数都转为负数了),如果大于被除数就往左移一位,来看下代码。
public int divide(int dividend, int divisor) {
boolean negative = dividend < 0 ^ divisor > 0; // 结果是否是负数。
// 为了方便计算,被除数和除数都转成负数。
dividend = dividend < 0 ? dividend : -dividend;
divisor = divisor < 0 ? divisor : -divisor;
int res = 0;// 两数相除的商。
// 被除数不能小于minDiv,否则往左移的时候会出现溢出。
int minDiv = Integer.MIN_VALUE >> 1;
// 被除数和除数都是负数。
while (dividend <= divisor) {
int tryDiv = divisor;// 用被除数减去除数的倍数。
int times = 1;// 记录减去的倍数。
// 尝试减去除数的倍数。
while (tryDiv >= minDiv && dividend <= tryDiv << 1) {
tryDiv <<= 1;
times <<= 1;// 被除数的倍数。
}
dividend -= tryDiv;
res -= times;
}
// 如果结果溢出,直接返回int范围内的最大值。
if (negative && res == Integer.MIN_VALUE)
return Integer.MAX_VALUE;
return negative ? -res : res;
}