题目
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2 ^31 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
来源:力扣(LeetCode)
https://leetcode-cn.com/problems/divide-two-integers
解题思路
首先,分析题目的难点在于环境只能存储32为有符号整数,所以边界问题应该是输入:-2^31,-1,这样我们在处理的时候就会超过int数据类型的取值范围。所以得用指令来判断是否超过int取值范围,我们使用下面的语句:
unsigned int p1 = dividend==INT_MIN?INT_MIN:abs(dividend);
unsigned int p2 = divisor==INT_MIN?INT_MIN:abs(divisor);
使用 (表达式1)?(表达式2):(表达式3)来判断,如果输入的除数和被除数等于INT_MIN,则变量就为INT_MIN,否则就取绝对值(因为取绝对值函数abs()返回的值不能超过int所能表示的大小)
然后另外一个需要解决的是符号问题,我们使用异或指令来判断:
bool emp = (dividend ^ divisor) < 0; //使用异或来判断除数和被除数是否异号
emp就为结果的符号位
一开始我的思路是使用减法,被除数循环减掉除数,知道被除数小于除数,则减法的次数就为商,但这种方法出现的问题是:当遇到被除数远大于除数时,效率很低
所以就使用移位:
首先我们得知道,数据左移一位相当于乘2,数据右移一位相当于除2,除法的运算就是(被除数-余数)/除数=商,那我们换个思路,除数=(被除数-余数)/商,那么问题就转换为:什么时候被除数/商最接近除数,那么此时就能求出商,具体代码如下:
class Solution {
public:
int divide(int dividend, int divisor) {
if (divisor == 1)
return dividend;
if (dividend == INT_MIN && divisor == -1)
return INT_MAX;
//首先要判断除数和被除数是否超过32位有符号数
unsigned int p1 = dividend==INT_MIN?INT_MIN:abs(dividend);
unsigned int p2 = divisor==INT_MIN?INT_MIN:abs(divisor);
if (p1 < p2)
return 0;
bool emp = (dividend ^ divisor) < 0; //使用异或来判断除数和被除数是否异号
unsigned int result = 0;
for (int i = 31; i >= 0; i--) {
if ((p1 >> i) >= p2) {//找出足够大的数2^n*divisor
result += ((unsigned int)1) << i;//将结果加上2^n
p1 -= p2 << i;//将被除数减去2^n*divisor
}
}
return emp ? -(int)result : (int)result;//符号相异取反
}
};
要注意的是:当被除数除于2的某次方时最接近除数,此时2^n次方有可能不是我们最后要求的结果,因为此时的余数还有可能能整除除数,所以需要将被减数减去此时的2 ^n,然后继续判断