LeetCode29-两数相除

这篇博客详细介绍了LeetCode第29题——两数相除的四种解题思路,包括将被除数一直加到0、除数倍增到超过被除数、利用快速乘法以及将除数表示成二进制的方法。每种方法都针对可能出现的溢出和负数进行了处理,并提供了相应的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode29-两数相除

Leetcode / 力扣

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值