题目要求返回两个int的整除结果,但是不能使用乘号、除号、取模运算符。
不能用除法,那我们用减法。将被除数不断减去除数,直到不能再减,减法的次数就是商。但是用脚趾头想都知道这样不行,因为如果除数为1的话要减到猴年马月?
既然是算除法,那试商肯定是需要用到乘法,但是不能用乘号。想一想int类型还可以怎么执行乘法?对了,就是左移运算符。左移一次相当于乘以2。那我们就可以仿造笔算竖式除法,从高位到低位试商,不过把十进制换成二进制。当被除数和除数都是正数时,就可以用下述的算法。
int k = divisor, j = 0, r = dividend, d = 0; //r为余数,d为商
while(k > 0 && dividend >= k) k <<= 1, j++;
k = divisor;
for(int i = j - 1 ; i >= 0 ; i--) {
if(r >= (k << i) ) {
r -= k << i;
d += 1 << i;
}
}
当然,题目中的int即可以为正,又可以为负,我们能不能取绝对值都变成正数处理呢?不行!因为负的int比正的int多出一个数,|INT_MAX| != |INT_MIN|
。当除数为INT_MIN时,商要么是0,要么是1,可以在函数开头就处理掉。当被除数为INT_MIN时,就没法特殊处理,也需要有试商的过程。所以我就把除数取和被除数相同的符号,然后对正数和负数都写一个试商的过程。
接着考虑结果溢出的问题。显然当除数为0的时候结果溢出。然后一想整除应该不会越除越大,所以就不会溢出了吗?错!当被除数为INT_MIN,除数为-1的时候,商为INT_MAX+1,也溢出了。除数非0的溢出就这一种情况。所以在函数开头特殊处理即可。
所以最后的代码是:
int divide(int dividend, int divisor) {
int sign;
if(dividend == 0)
return 0;
if(divisor == 0)
return INT_MAX;
if(divisor == dividend)
return 1;
if(divisor == INT_MIN)
return 0;
if(dividend == INT_MIN && divisor == -1)
return INT_MAX;
if((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0))
sign = 1;
else
sign = -1, divisor = -divisor;
if(dividend > 0) {
if(divisor > dividend)
return 0;
int k = divisor, j = 0, r = dividend, d = 0;
while(k > 0 && dividend >= k) k <<= 1, j++;
k = divisor;
for(int i = j - 1 ; i >= 0 ; i--) {
if(r >= (k << i) ) {
r -= k << i;
d += 1 << i;
}
}
return d * sign;
}
else {
if(divisor < dividend)
return 0;
int k = divisor, j = 0, r = dividend, d = 0;
while(k < 0 && dividend <= k) k <<=1, j++;
k = divisor;
for(int i = j - 1 ; i >= 0 ; i--) {
if(r <= (k << i)) {
r -= k << i;
d += 1 << i;
}
}
return d * sign;
}
}
总结:此类题目一定要牢记|INT_MAX| != |INT_MIN|
。