原题
本题可以基于减法求整数的除法。计算机的电路系统只支持加法(如果负数则负数前加+),便于简化设计,对外提供一致接口。
解题思路
常规解法
15/2 可以转化成如下步骤:
15 - 2 = 13
13 - 2 = 11
11 - 2 = 9
9 - 2 = 7
7 - 2 = 5
5 - 2 =3
3 - 2 = 1
1 - ?
剩余的被除数小于除数,则停止运算。15 最多能被 为 2 的 7次方数整除。所以商为7, 余数为1。
把上面推到过程写成代码可得:
public int divideNormal(int dividend, int divisor) {
int quotient = 0;
while (dividend > divisor) {
dividend = dividend - divisor; // 如果被除数和除数相差太大,则更加耗时。
quotient ++;
System.out.println("result = " + quotient);
}
return quotient;
}
}
这种常规解法如果遇到被除数和除数相差太大,那么减法运算将非常耗时。所以我们得想想其他办法,让减法运算变少。
优化解法
public int dividerCore(int dividend, int divisor) {
int result = 0;
while (dividend > divisor) {
int quotient = 1;
int value = divisor;
while(dividend >= value + value) {
quotient += quotient; // 商也翻倍
value = value + value; // 除数的大小翻倍
}
result += quotient;
dividend -= value
}
System.out.println("result = " + result);
}
考虑边界情况
1. 商溢出
如果商大于Int的最大值,则发生整型溢出。 即被除数为最小值,除数为-1
if(dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE; // -2的31次方 / ( -1) = 2的31次方,此时商已经大于Int的最大值2的31次方 - 1。
}
2.正数和负数
为了方便,我们可以先把被除数和除数,按照正数参与运算,得到除法的结果后,再根据原值,来确定最终的商是正数还是负数。
由于负数转成正数,会溢出。 如最小值负数-2的31次方转成正数就溢出了。所以我们把被除数和除数都转成负数参与运算。
最终代码如下:
public int divide(int dividend, int divisor) {
if (dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE; // -2的31次方 / ( -1) = 2的31次方,此时商已经大于Int的最大值2的31次方 - 1。
}
int negative = 2;
if (dividend < 0) {
negative--;
dividend = -dividend;
}
if (divisor < 0) {
negative--;
divisor = -divisor;
}
int result = divideCore(dividend, divisor);
return negative == 1 ? -result : result;
}
public int divideCore(int dividend, int divisor) {
int result = 0;
while (dividend > divisor) {
int quotient = 1;
int value = divisor;
//除数按倍数减小
while (value > (Integer.MIN_VALUE / 2) && dividend >= (value + value)) {
quotient += quotient;
value += value;
}
result += quotient;
dividend -= value;
}
return result;
}
总结
整数的除法,主要考察基于减法做整数的除法运算。需要您掌握Int的内存空间范围以及整数的加法运算(包括补码的知识点)。