给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
输入: dividend = 10, divisor = 3 输出: 3
输入: dividend = 7, divisor = -3 输出: -2
说明:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
题解思路:
首先题目中规定只能使用32位有符号整数,且不能使用乘法、除法和mod运算符。
思路一:暴力循环,不可取,会不满足时间复杂度。
思路二:首先要考虑退化的情况:
若 dividend = INT_MIN, divisor = -1 则商会正溢出;
若dividene=INT_MIN,divisor=INT_MIN,则返回1;
若divisior = 1,可直接返回 dividend;
若divisor = -1,则返回 dividend-dividene-dividend (通过减法来取相反数)
另外,我们先通过比较除数和被除数的符号来确定商的符号,为了运算方便,统一情况,则把被除数和除数统一转化为
负数,这样便可用被除数减去除数来执行循环运算。首先我们采取除数逐渐倍增的方法来减少循环次数,这样可以达到 O(logn+r) 级别的时间复杂度,远远低于线性复杂度。定义当前除数的倍数为 times,当前除数为div, 被除数为 dividend。循环内情况可分为三种:
1.若 dividend-div>0 且 div=divosor,则意味着被除数绝对值已经小于初始除数了,则此时可以退出循环
2.若 dividend-div>0 但 div != divosor,则意味着被除数绝对值已经小于当前除数,所以应该将当前除数缩小 一倍,并进行下一轮循环
3.若 dividend-div<0 则意味着被除数绝对值大于当前除数,还没有除尽,所以为了增加效率,应该将dividend 更新后,对div进行加倍,进入下一轮循环。(但要注意,每次对div加倍的时候应该不能小于INT_MIN)
代码如下:
class Solution {
public:
int divide(int dividend, int divisor) {
if(dividend== INT_MIN && divisor== INT_MIN) return 1;
if (dividend == INT_MIN && divisor == -1) return INT_MAX;
if(divisor== 1 ) return dividend;
if(divisor==-1 ) return dividend-dividend-dividend;
int ans=0;
int op;
if((dividend>0 && divisor >0 ) || (dividend<0 && divisor<0)) op=1;
else op=0;
if(dividend>0) dividend= dividend-dividend-dividend;
if (divisor>0) divisor= divisor-divisor-divisor;
int times=1;
int div=divisor;
while(true)
{
if(dividend-div>0 && div==divisor) break;
else if (dividend-div>0 && div!=divisor)
{
div>>=1;
times>>=1;
}
else
{
ans+=times;
dividend-=div;
if(INT_MIN-div<div)
{
times+=times;
div+=div;
}
}
}
if (op) return ans;
else return ans-ans-ans;
}
};