29. Divide Two Integers(两数相除)
1. 题目描述
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333…) = -2
提示:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31− 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。
2. 减法(Subtraction, Time Limit Exceeded)
2.1 解题思路
最简单的思路就是:被除数每减去一次除数,计数加一,当被除数小于0的时候,根据被除数和除数的符号返回总计数即可。只是需要考虑一个很极端的情况:
被除数:INT_MIN,除数:-1
这种情况的结果是INT_MAX + 1,明显超过了整型能表示的最大值,所以这里需要额外判断一下。但是这个方法当被除数很大,除数很小的时候,比如刚才的例子,非常的耗时,所以超时了。
2.2 实例代码
class Solution {
public:
int divide(int dividend, int divisor) {
bool minusOne = dividend >= 0 ? false : true, minusTwo = divisor >= 0 ? false : true;
long long dividendTemp = dividend, divisorTemp = divisor;
if (dividendTemp < 0) dividendTemp = -dividendTemp;
if (divisorTemp < 0) divisorTemp = -divisorTemp;
long long count = 0;
while (dividendTemp >= 0) { count++; dividendTemp -= divisorTemp; }
count--;
if (minusOne && minusTwo || !minusOne && !minusTwo) {
if (count >= INT_MAX) return INT_MAX;
else return count;
}
return -count;
}
};
3. 位操作(Bit Manipulation)
3.1 解题思路
上面的思路中是一个个地减去除数,如果想要节约时间,能不能一次性减去多个除数呢?也就是减去除数的倍数,但是题目要求不能使用乘法、除法和模运算,所以我们可以考虑使用位运算(左位移和右位移):
- 整型左位移1位,相当于原数 /= 2;并且移动的时候,最低位舍弃,负数最高位补1,正数最高位补0;
- 整型右位移1位,相等于原数 *= 2;并且最低位补0,最高位舍弃;
比如1二进制表示为0001(省略前面的0),左位移一位变成0000 = 0,右位移一位变成0010 = 2。比如-1二进制表示为1111(省略前面的1),左位移一位不变还是1111 = -1,右位移一位变成1110 = -2;
所以我们可以利用左位移的特性,让被除数减去偶数倍的除数,操作步骤如下:
- 初始化一个新的被除数(dividendTemp),一个新的除数(divisorTemp ),一个答案(ans),一个倍数(times)。
- 并且dividendTemp和divisorTemp 需要绝对值一下,保证计算时都是正数;
- 然后判断一下结果是复数还是正数;
- 如果除数是1,根据符号直接返回dividendTemp即可;
- 接下来,判断一下dividendTemp是不是大于两倍的divisorTemp (divisorTemp << 1),如果是divisorTemp扩大两倍,times也扩大两倍;一直重复这一步直到dividendTemp不在大于两倍的divisorTemp,跳出循环的时候ans += times;
- 接下来,判断一下dividendTemp是不是大于等于divisorTemp。如果是只需要ans += 1即可,因为这时dividendTemp最多只比divisorTemp大一倍
- 当dividendTemp < divisorTemp,已经做完除法,根据符号返回ans即可;
举个例子:dividendTemp = 14,divisorTemp = 3。14大于两倍的3(= 6),也大于两倍的6(= 12),所以14 - 12 = 2 < 3,循环结束,我们返回4即可。
其实这个思路可以理解成用除数的偶数倍来做除法,最后返回的答案就是除数是被除数的多少倍。当我们保证被除数不大于两倍的除数,这时被除数要么小于除数,直接舍去结果即可(因为整型计算,没有小数);被除数要么等于大于一倍的除数,小于两倍的除数,这时结果再加一即为最终结果。
3.2 实例代码
3.2.1 迭代实现
class Solution {
public:
int divide(int dividend, int divisor) {
if (dividend == INT_MIN && divisor == -1) return INT_MAX;
long dividendTemp = labs(dividend), divisorTemp = labs(divisor), ans = 0;
bool ifPositive = (dividend < 0) ^ (divisor < 0) ? false : true; // 结果是正数还是负数,异或运算:相同为0,不同为1
if (divisorTemp == 1) return ifPositive ? dividendTemp : -dividendTemp;
while (dividendTemp >= divisorTemp) {
long divisorMultiply = divisorTemp, times = 1;
while (dividendTemp >= divisorMultiply << 1) {
divisorMultiply <<= 1;
times <<= 1;
}
ans += times;
dividendTemp -= divisorMultiply;
}
return ifPositive ? ans : -ans;
}
};
3.2.2 递归实现
class Solution {
public:
int divide(int dividend, int divisor) {
long dividendTemp = labs(dividend), divisorTemp = labs(divisor);
if (dividendTemp < divisorTemp) return 0;
long divisorMulply = divisorTemp, times = 1, ans = 0;
while (dividendTemp > (divisorMulply << 1)) { divisorMulply <<= 1; times <<= 1; }
ans += times + divide(dividendTemp - divisorMulply, divisorTemp);
if ((dividend < 0) ^ (divisor < 0)) ans = -ans;
return ans >= INT_MAX ? INT_MAX : ans;
}
};
4. 参考资料

本文深入探讨LeetCode第29题“两数相除”的解题策略,介绍不使用乘除法和mod运算符进行整数除法的两种方法:减法和位操作。通过实例代码展示减法方法的超时问题,并详细解析位操作如何优化效率。
524

被折叠的 条评论
为什么被折叠?



