LeetCode29-两数相除
29. 两数相除:
给定两个整数,被除数
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
提示:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1]。本题中,如果除法结果溢出,则返回 2^31 − 1。
解题思路1:
最开始想到的是将被除数一直加到0或者减到0,但是当除数很小时会超时。但在这个过程中想到了所有的都做负号处理,避免溢出
超时代码如下:
class Solution {
public:
int divide(int dividend, int divisor) {
if(dividend==INT_MIN&&divisor==-1)
return INT_MAX;
if(dividend==0)
return 0;
if(divisor==1)
return dividend;
if(divisor==-1)
return -dividend;
//由于负数的范围大一些,除数和被除数全部变成负数
int dived=dividend;
int div=divisor;
if(dived>0)
dived=-dived;
if(div>0)
div=-div;
int ans=0;
//这里要判断是否已经被减到0了(其实是加,因为减一个负数就是加上这个数),否则再减一个div(即再加一个div),当div为INT_MIN时,加上这个数就会溢出
while(dived!=0&&dived-div<=0) {
dived-=div;
++ans;
}
//判断答案符号
if((dividend<0&&divisor>0) || (dividend>0&&divisor<0))
ans=-ans;
return ans;
}
};
解题思路2:
优化时间,将除数倍增到超过被除数。
越界问题只要对除数是1和-1单独讨论
关于如何提高效率快速逼近结果:
举个例子:11 除以 3 。
首先11比3大,结果至少是1, 然后我让3翻倍,就是6,发现11比3翻倍后还要大,那么结果就至少是2了,那我让这个6再翻倍,得12,11不比12大,吓死我了,差点让就让刚才的最小解2也翻倍得到4了。但是我知道最终结果肯定在2和4之间。也就是说2再加上某个数,这个数是多少呢?我让11减去刚才最后一次的结果6,剩下5,我们计算5是3的几倍,也就是除法,看,递归出现了。
参考连接
代码如下:
class Solution {
int cal(int a,int b) {
//这里采用倍增的方法,将b一直翻倍到达a
int sum=1;
int base=b;
//因为是负数,所以是小于
if(b<a)
return 0;
while((long long)b+b>=a && (long long)b+b<0) { //溢出后会变会正数,不再小于0,这里不加longlong应该也是可以的,因为溢出之后就变成正数了,但编译不过,java可以,所以c++只能在前面加long或者longlong
b+=b;
sum+=sum;
}
return sum+cal(a-b,base); //递归计算中间多余的
}
public:
int divide(int dividend, int divisor) {
if(divisor==1)
return dividend;
if(divisor==-1){
if(dividend==INT_MIN)//溢出
return INT_MAX;
return -dividend;
}
if(dividend==0)
return 0;
//由于负数的范围大一些,除数和被除数全部变成负数,并记录答案的符号
int sign=1;
if((dividend<0&&divisor>0) || (dividend>0&&divisor<0))
sign=-1;
if(dividend>0)
dividend=-dividend;
if(divisor>0)
divisor=-divisor;
int ans=cal(dividend,divisor);
return sign==1?ans:-ans;
}
};
解题思路3:
利用快速乘法来实现乘法,因为快速乘法只有加减,所以满足题意。
我们记被除数为 X,除数为 Y,并且根据思路1 X 和 Y 都是负数。我们需要找出 X/Y的结果 Z。Z 一定是正数或 0。我们需要找到最大的Z满足y*z>=x。(因为这里x,y是负数,符号需要反过来)
参考官方题解
代码如下:
class Solution {
//利用快速乘法去检验
bool check(int x,int y,int mid) {
//由于x和y都小于0
//如果满足y*mid>=x,则返回true,mid还可以增加
int result=0; //res保存y*mid
while(mid) { //这里只能用mid判断,因为mid是大于0的,-1移位才会变为0
if(mid&1) {
//由于是负数,所有res+y<x,就不满足条件
if(result<x-y)
return false;
result+=y;
}
if(mid!=1 && y<x-y) //如果不是最后一次加,但也已经比x小了,则返回false
return false;
if(mid!=1)
y+=y; //避免溢出INT_MIN
mid>>=1;
}
return true;
}
//计算x/y,此时x,y都为负数
int cal(int x,int y) {
int ans=0;
int l=0,r=INT_MAX;
while(l<=r) {
int mid=l+((r-l)>>1); //避免int溢出
if(check(x,y,mid)) {
ans=mid;
//注意溢出
if(mid==INT_MAX)
break;
l=mid+1;
}
else {
r=mid-1;
}
}
return ans;
}
public:
int divide(int dividend, int divisor) {
if(divisor==1)
return dividend;
if(divisor==-1){
if(dividend==INT_MIN)//溢出
return INT_MAX;
return -dividend;
}
if(dividend==0)
return 0;
//由于负数的范围大一些,除数和被除数全部变成负数,并记录答案的符号
int sign=1;
if((dividend<0&&divisor>0) || (dividend>0&&divisor<0))
sign=-1;
if(dividend>0)
dividend=-dividend;
if(divisor>0)
divisor=-divisor;
int ans=cal(dividend,divisor); //利用二分计算除法
return sign==1?ans:-ans;
}
};
解题思路4:
考虑以下公式(还是根据解题思路1,X,Y都变成负数):
X / Y = Z
X = Y * Z
X = Y * (20+21+22+…+2i) (将Z表示成二进制)
X = Y * 2^0 + Y * 2^0 + Y * 2^0 +… (由乘法分配律得)
因此,只用将所有的Y * 2^i计算到严格小于X,因为是负数。
在用所有的Y * 2^i表示成X就行。
和计算一个数的二进制表示一样。
参考官方题解
代码如下:
class Solution {
int cal(int x,int y) {
vector<int>expre={y};
int now=y;
//防止溢出
while(now>=x-now) {
now+=now;
expre.push_back(now);
}
int len=expre.size();
int ans=0;
for(int i=len-1;i>=0;--i) {
if(x<=expre[i]) {
x-=expre[i];
ans+=(1<<i);
}
}
return ans;
}
public:
int divide(int dividend, int divisor) {
if(divisor==1)
return dividend;
if(divisor==-1){
if(dividend==INT_MIN)//溢出
return INT_MAX;
return -dividend;
}
if(dividend==0)
return 0;
//由于负数的范围大一些,除数和被除数全部变成负数,并记录答案的符号
int sign=1;
if((dividend<0&&divisor>0) || (dividend>0&&divisor<0))
sign=-1;
if(dividend>0)
dividend=-dividend;
if(divisor>0)
divisor=-divisor;
int ans=cal(dividend,divisor); //利用二分计算除法
return sign==1?ans:-ans;
}
};