leetcode:29. 两数相除

本文详细解析了LeetCode题目的29题,涉及如何用减法和位运算实现两数相除,特别关注负数情况下的优化处理,包括转换策略和二分法技巧。重点讲解了如何避免溢出并提高效率,适合前端、后端开发者学习。

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

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    int divide(int dividend, int divisor) {

    }
};

题目解析

分析

  • 题目要求不能用乘法、除法和mod。那么只能用加法、减法、或者位运算了。要实现两数相除的功能我们可以用减法、位运算。这里用减法
  • 题目说了,除数disisor被除数dividend可能是正数、负数
    • 为简化代码和逻辑,我们可以先把除数disisor被除数dividend都转换成正正或者负负,再进行统一的逻辑处理,同时记录下最终结果是正号还是负号。
    • 如果均转换为正正,那就有可能溢出(INT_MIN转换为INT_MAX时),但是把任意正整数转换为负数是不会溢出的。因此我们把disisor和dividend均转成负数
class Solution {
    /**
    * 使用减法实现两个负整数的相除操作
    * 
    * dividend:被除数
    * divisor:除数
    */
    int divideCore(int dividend, int divisor) {

    }

public:
    /**
     * 两数相除
     * 
     * dividend 被除数
     * divisor 除数
     */
    int divide(int dividend, int divisor) {
        // 处理整型溢出问题,-2³¹=0x80000000
        if (dividend == 0x80000000 && divisor == -1) {
            return INT32_MAX;
        }

        // 用来记录除数和被除数是负整数的个数,初始化为2
        int negative = 2;

        // 如果被除数是正整数,则转换成对应的负整数
        if (dividend > 0) {
            negative--;
            dividend = -dividend;
        }

        // 如果除数是正整数,则转换成对应的负整数
        if (divisor > 0) {
            negative--;
            divisor = -divisor;
        }

        int result = divideCore(dividend, divisor);
        return negative == 1 ? -result : result;
    }
};

我们接下来继续分析两个负整数相除的处理,以 dividend/divisor 为例子,当 dividend 小于等于 divisor 时,我们循环执行 (dividend-divisor) 并计数:

/**
    * 使用减法实现两个负整数的相除操作
    * 
    * dividend:被除数
    * divisor:除数
    */
    int divideCore(int dividend, int divisor) {
        int quotient = 0;
        while (dividend <= divisor){
            quotient++;
            dividend -= divisor;
        }
        return quotient;
    }

但是有一个问题,当 dividend 远小于 divisor 时,这段代码执行会花费太长时间,导致超时

因此,我们得想其他改进的方法,整体思路还是通过减法实现,但为了缩小想减的次数,我们借鉴二分法

  • 当被除数 dividend 小于除数 divisor 时,继续比较判断dividend 是否小于divisor 的 2 倍,如果是,则继续判断dividend 是否小于divisor 的 4 倍、8 倍等
  • 如果dividend 最多小于divisor 的 2ᵏ 倍,那么将被除数减去除数的 2ᵏ 倍,然后将剩余的被除数重复前面的步骤
 /**
    * 使用减法实现两个负整数的相除操作
    * 
    * dividend:被除数
    * divisor:除数
    */
    int divideCore(int dividend, int divisor) {
        int ans = 0;
        while (dividend <= divisor){
            int quo = 1;
            int value = divisor;
            // 注意题目要求,只能使用加法和减法
            // 因此,通过value+value实现value*2的效果
            while (dividend <= value + value){
                quo += quo;
                value += value;
            }
            
            ans += quo;
            dividend -= value;
        }
        return ans;
    }
  • 但是value + value可能溢出,所以我们需要限制一下value的大小,val是负数,而最小的 int 负数是 -2³¹(0x80000000),它的一半是 -2³¹/2=-2³⁰(0xc0000000)
 /**
    * 使用减法实现两个负整数的相除操作
    * 
    * dividend:被除数
    * divisor:除数
    */
    int divideCore(int dividend, int divisor) {
        int ans = 0;
        while (dividend <= divisor){
            int quo = 1;
            int value = divisor;
            // 注意题目要求,只能使用加法和减法
            // 因此,通过value+value实现value*2的效果
            while (value >= 0xc0000000 && dividend <= value + value){
                quo += quo;
                value += value;
            }
            
            ans += quo;
            dividend -= value;
        }
        return ans;
    }

位运算

class Solution {
public:
    int divide(int dividend, int divisor) {
        long m = labs(dividend), n = labs(divisor), res = 0;
        if (m < n) return 0;
        long t = n, p = 1;  // p = 1结构至少是1
        while (m > (t << 1)) {
            t <<= 1;  // 最小解翻倍
            p <<= 1;  // 当前测试的值也翻倍
        }
        res += p + divide(m - t, n);
        if ((dividend < 0) ^ (divisor < 0)) res = -res;
        return res > INT_MAX ? INT_MAX : res;
    }
};

11 除以 3

  • 首先11比3大,结果至少是1
  • 然后让3翻倍,就是6,发现11比3翻倍后还要大,那么结果至少是2了
  • 这个6再翻倍,得12,11不比12大
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值