题目描述
https://leetcode-cn.com/problems/divide-two-integers/description/
给定两个整数,被除数 dividend
和除数 divisor
。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend
除以除数 divisor
得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
思路
需要判断结果的符号
- 在数的二进制表示中,首位为1表示负数,0表示正数;
- 那么,通过两数异或 (^) 操作,即可知道结果是否为正:负数 ^ 正数 = 负数
初始的时候,需要将被除数和除数都取绝对值,得到
tmp_dividend
和tmp_divisor
- 这里我们要用到位运算实现的加法运算;
- 取相反数的操作为:按位取反再加1,即
add(~x, 1)
- 需要注意的是,定义这两个变量的类型为
long long
,因为该题的测试用例中包含了INT_MIN
与INT_MAX
的情况 - 如果输入为
-2147483648
,并且定义的变量类型为int
,那么得到的结果与预期不符,原因在于-2147483648
相反数为2147483648
>INT_MAX
产生了溢出 (哭晕在厕所,这道题的case真的很严格(ಥ_ಥ))
主要思路就是用被除数减去除数,用变量
res
保存解,也就是减去了多少个除数- 数m左移n位,结果为m × 2n
- 为了加快速度,可以每次减去除数x2,通过左移 (<<) 运算实现:
tmp << 1
- 内层循环用来将tmp不断逼近
tmp_dividend
,当tmp_dividend-tmp>=0
时,说明tmp
的移位操作还没超过tmp_dividend
,并且将tmp_dividend
更新为当前的被除数tmp_dividend
与tmp
的差; 变量res
初始化为1,那么每当循环执行了一次之后,相应的,res << 1
(除数左移1次,res也左移一次) - 如果左移一位的除数过大,则退出内层循环,除数
tmp
还原为一开始的tmp_divisor
- 外层循环中,当
tmp_dividend >= tmp_divisor
,说明当前的被除数tmp_dividend
仍包含至少一个除数tmp_divisor
,可以继续 - 比如:7 / 3,3 << 1之后为6,res << 1为2,相当于此时减去的是两个3
因此总结起来就是两层while循环,内层循环用来试探不断逼近
tmp_dividend
,当用来试探的tmp
超过了tmp_dividend
时,退出内层循环,当前被除数重新赋值为一开始的tmp_divisor
代码
long long add(long long a, long long b)
{
long long sum = a;
long long carry = b;
while (carry)
{
long long tmps = sum;
sum = tmps ^ carry;
carry = (tmps & carry) << 1;
}
return sum;
}
int divide(int dividend, int divisor) {
// 取绝对值
long long tmp_dividend = dividend < 0 ? add(~dividend, 1) : dividend;
long long tmp_divisor = divisor < 0 ? add(~divisor, 1) : divisor;
long long res = 0;
while (tmp_dividend >= tmp_divisor){
long long tmp = tmp_divisor;
int i = 1;
while (tmp_dividend - tmp >= 0){
tmp_dividend = tmp_dividend - tmp;
res += i;
i = i << 1;
tmp = tmp << 1;
}
}
// 如果商小于0,返回res的相反数
if ((dividend ^ divisor) < 0)
{
res = add(~res, 1);
}
res = res > INT_MIN ? res : INT_MIN;
return res < INT_MAX ? res : INT_MAX;
}