题目名,两数相除,表面看起来人畜无害。结果…,他喵的提交十几次才过。
题目描述(题目难度,中等)
给定两个整数,被除数 dividend
和除数 divisor
。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend
除以除数 divisor
得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [ − 2 31 −2^{31} −231, 2 31 − 1 2^{31} − 1 231−1]。本题中,如果除法结果溢出,则返回 2 31 − 1 2^{31} − 1 231−1。
题目求解
解法一
最开始看题目也没什么时间复杂度的要求,以为题目难点主要在于不能使用乘除和取余运算。于是想到一个直白的试减法就提交了,试减法的思路就是每次循环都将被除数减去一个除数,这样最终被除数被减去的次数就是商。
其实这个解法在英文版官网上是可以提交通过的,这样也比较符合划定的中等难度的级别。但到了中文版,就会说超出时间限制,无法通过。所以没办法,只能优化了这个解法,最终在中文版网站提交通过。
这个试减法,我是将输入的被除数和除数统一转换为负数,再进行试减的。为什么这样呢,我们平常不是习惯于正数的计算吗。其实我最开始也是统一转化为正数来做的,但这样做有一个问题就是,如果被除数为 Integer.MIN_VALUE
,则它无法转化为对应的正数。需要注意:Integer.MIN_VALUE == -Integer.MIN_VALUE
。
class Solution {
public int divide(int dividend, int divisor) {
if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 题意
if(divisor == Integer.MIN_VALUE){ // **此处如不特判,后面会陷入死循环
if(dividend == Integer.MIN_VALUE) return 1;
else return 0;
}
if(divisor == -1) return -dividend;
if(divisor == 1) return dividend;
boolean d = true; // 同号标志
if(dividend <= 0 && divisor > 0){
d = false;
divisor = -divisor;
}else if(dividend >= 0 && divisor < 0){
d = false;
dividend = -dividend;
}else if(dividend >= 0 && divisor > 0){
dividend = -dividend;
divisor = -divisor;
}
int result = 0;
while(true){
dividend -= divisor;
if(dividend > 0) return d == true ? result : -result;
++result;
}
}
}
解法二
加速的试减法,使用移位运算来加速试减。
class Solution {
public int divide(int dividend, int divisor) {
if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 题意
if(divisor == Integer.MIN_VALUE){ // **此处如不特判,后面会陷入死循环
if(dividend == Integer.MIN_VALUE) return 1;
else return 0;
}
if(divisor == -1) return -dividend;
if(divisor == 1) return dividend;
boolean d = true; // 同号标志
if(dividend <= 0 && divisor > 0){
d = false;
divisor = -divisor;
}else if(dividend >= 0 && divisor < 0){
d = false;
dividend = -dividend;
}else if(dividend >= 0 && divisor > 0){
dividend = -dividend;
divisor = -divisor;
}
int div = divisor, k = 1, result = 0;
final int bound = Integer.MIN_VALUE >> 1;
while(true){
dividend -= div;
if(dividend > 0){
if(k == 1) return d == true ? result : -result;
else{
dividend += div;
div = divisor;
k = 1;
continue;
}
}
result += k;
if(div >= bound){ // ****溢出判断,不能让 div 左移后溢出了
div <<= 1;
k <<= 1;
}
}
}
}
如果感觉迷糊,看个运算实例就懂了,
假设被除数 dividend = 20,除数 divisor = 3。
加速试减法的流程如下:
- dividend = 20 - 3 = 17,divisor = 3,k = 1,result = 1(result 为最终要返回的结果)
- dividend = 17 - 3*2 = 11,divisor = 6,k = 2,result = 3(将 k 累加进 result)
- dividend = 11 - 6*2 = -1,dividend < 0,则 divisor = 3,k = 1
- dividend = 11 - 3 = 8,divisor = 3,k = 1,result = 4
- dividend = 8 - 3*2 = 2,divisor = 6,k = 2,result = 6
- dividend = 2 - 6*2 = -10,dividend < 0,则 divisor = 3,k = 1
- dividend = 2 - 3 = -1,dividend < 0,又 k == 1,算法结束,result = 6
所以 20 除 3 的商就为 6。