1、题目描述
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
2、示例
输入: dividend = 10, divisor = 3
输出: 3
输入: dividend = 7, divisor = -3
输出: -2
说明:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。
3、题解
如果单靠被除数慢慢减除数,一直减到被除数小于除数,这种方法时间复杂度为O(n)超时。用增倍除数的方法,举个例子如果被除数16,除数3,用我们上面的方法要遍历5次,接下来,我们使用不断增倍除数,比如:
被除数 除数
16 3
13 6
7 12
发现除数大于被除数,除数还原为原始值重新开始
7 3
4 3
1 3
虽然这个例子遍历次数相等,对于较大的数,可以减少时间复杂度。时间复杂度为O(logn)。
除此之外,还要将dividend和divisor都转换为负数计算,因为负数最小值是-pow(2,31)比正数最大值是pow(2,31)-1范围要大,如果转为正数计算-pow(2,31)转不了pow(2,31)。特殊考虑两种情况-pow(2, 31)除以-1等于pow(2, 31)超过整数最大范围溢出,返回整数最大值;-pow(2, 31)除以1等于-pow(2, 31),因为转为负数计算-pow(2, 31)除以-1等于pow(2, 31)溢出。
- 不断循环,被除数减mutiple*除数,商+=multiple,一直减到dividend>divisor_,此时dividend为余数
- multiple记录divisor的倍数,通过dividend减multiple倍的divisor,商ret+=multiple,商等于所有除数的倍数之和
- 当divisor<=dividend,dividend不断减除数,除数翻倍,multiple翻倍
- 当divisor>dividend,divisor还原为原始值divisor_,multiple为divisor的倍数也初始为1
#include<iostream>
#include<math.h>
using namespace std;
class Solution {
public:
int divide(int dividend, int divisor) {
//简化版本:增倍除数法
long res=0,a=abs(dividend),b=abs(divisor);
while(a>=b){
long cnt=1,base=b;
while(a>=base)
{
res+=cnt;
a-=base;
cnt<<=1;
base<<=1;
}
}
res=((dividend<0)^(divisor<0))?-res:res;
return (INT_MAX<res||res<INT_MIN)?INT_MAX:res;
}
};
class Solution1 {
public:
int divide(int dividend, int divisor) {
int ret = 0; //返回的结果值商
int flag = -1;
//对最后的商符号判断flag=1商为正数,flag=-1商为负数
if (dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0)
flag = 1;
//-pow(2, 31)除以-1等于pow(2, 31)超过整数最大范围溢出,返回整数最大值
if (dividend == -pow(2, 31) && divisor == -1)
return pow(2, 31) - 1;
//-pow(2, 31)除以1等于-pow(2, 31),因为转为负数计算-pow(2, 31)除以-1等于pow(2, 31)溢出
if (dividend == -pow(2, 31) && divisor == 1)
return -pow(2, 31);
//将dividend和divisor都转换为负数计算,因为负数最小值是-pow(2,31)比正数最大值是pow(2,31)-1范围要大,如果转为正数计算-pow(2,31)转不了pow(2,31)
if (dividend > 0)
dividend = -dividend;
if (divisor > 0)
divisor = -divisor;
//记录原始divisor,因为divisor在计算过程中不断翻倍,当divisor>dividend,divisor还原为原始值divisor_
int divisor_ = divisor;
//如果dividend>divisor,商为0
if (dividend > divisor)
return 0;
//multiple记录divisor的倍数,通过dividend减multiple倍的divisor,商ret+=multiple
int multiple = 1;
//不断循环,一直减到dividend>divisor_,此时dividend为余数
while (dividend <= divisor_)
{
if (dividend <= divisor)
{
dividend = dividend - divisor;
ret += multiple; //商等于所有除数的倍数之和
//防止溢出,-1100540749除以-1090366779,除数翻倍就会溢出
if (divisor < -pow(2, 30))
divisor = -pow(2, 31);
else
divisor += divisor; //除数翻倍,可以divisor<<=1左移一位数值翻倍
multiple += multiple; //除数翻倍,multiple为除数的倍数,也翻倍,可以multiple<<=1左移一位数值翻倍
}
//当divisor>dividend,divisor还原为原始值divisor_,multiple为divisor的倍数也初始为1
else
{
divisor = divisor_;
multiple = 1;
}
}
//flag=1商为正数,flag=-1商为负数
if (flag == 1)
return ret;
else
return -ret;
}
};
int main()
{
int dividend = -pow(2,31);
int divisor = 2;
Solution solute;
cout << solute.divide(dividend, divisor) << endl;
return 0;
}