题目描述
给定两个整数,被除数 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
来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/di…
分析
根据题目要求不使用乘法、除法和mod运算,首先想到了位运算(循环加减法太慢了,不考虑)和转换除法运算为加减两个思路。
我们先考虑第二种转换除法,将其转换为exp(log(a)-log(b))的形式,公式与a/b一样,但是实际计算机执行中先进行log(a)和log(b),然后相减,最后再执行exp(x),中间经历几次除不尽导致结果与实际可能偏差。如-231/3=-77,如果使用上述办法求出的是-76。因此我们考虑用位运算:
任何一个数k可以被表示为:k = b0 * 2^0 + b1 * 2^1 + b2 * 2^2 + ... + bn * 2^n + ... 例如231/3,b0...bn都是3,我们要求的就是需要多少个3。 例:59/3=19 以2^0 * 3为计量,需要计算19次59-3 以2^1 * 3为计量,需要计算9次59-6和1次59-3 以此类推
实现
- 方法一:使用了强制转换为long,防止溢出
// 转化为对数做减法
int divide(int dividend, int divisor)
{
// int范围整数比负数小1,如果除数为INT_MIN,除数为-1,那么求的值为2^31,溢出了,溢出返回2^31-1
if (dividend == INT_MIN && divisor == -1) {
return INT_MAX;
}
// 把负号保存下来
int sym = (dividend < 0) ^ (divisor < 0); // 异或运算,被除数和除数负号不相同时返回真(1)
long x = labs((long)dividend);
long y = labs((long)divisor);
long result = 0;
while (x >= y) {
long tmp = y;
long res = 1;
while (x >= (tmp << 1)) {
tmp <<= 1;
res <<= 1; // tmp中包含多少个y
}
result += res;
x -= tmp;
}
if (sym == 1) {
result *= -1;
}
if (result > INT_MAX) {
return INT_MAX;
}
return result;
}
- 方法二:不使用强制转换,而是提前判断边界,并且计算时右移而不是左移
// 转化为对数做减法
int divide(int dividend, int divisor)
{
int result = 0;
if (divisor == INT_MIN) { // 除数边界值特殊处理
if (dividend == INT_MIN) {
return 1;
} else {
return 0;
}
}
if (dividend == INT_MIN) { // 被除数边界值特殊处理
if (divisor == -1) {
return INT_MAX;
} else if (divisor == 1) {
return INT_MIN;
}
dividend += abs(divisor); // 先执行一次加操作,避免abs转换溢出
result++;
}
// 把负号保存下来
int sym = (dividend < 0) ^ (divisor < 0); // 异或运算,被除数和除数负号不相同时返回真(1)
int x = abs(dividend);
int y = abs(divisor);
while (x >= y) {
int tmp = y;
int res = 1;
while (tmp < (x >> 1)) {
tmp <<= 1;
res <<= 1; // tmp中包含多少个y
}
result += res;
x -= tmp;
}
if (sym == 1) {
result *= -1;
}
if (result > INT_MAX) {
return INT_MAX;
}
return result;
}