29. Divide Two Integers(两数相除)两种解法(C++ & 注释)

1. 题目描述

给定两个整数,被除数 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。

题目链接:中文题目英文题目

2. 减法(Subtraction, Time Limit Exceeded)

2.1 解题思路

最简单的思路就是:被除数每减去一次除数,计数加一,当被除数小于0的时候,根据被除数和除数的符号返回总计数即可。只是需要考虑一个很极端的情况:

被除数:INT_MIN,除数:-1

这种情况的结果是INT_MAX + 1,明显超过了整型能表示的最大值,所以这里需要额外判断一下。但是这个方法当被除数很大,除数很小的时候,比如刚才的例子,非常的耗时,所以超时了。

2.2 实例代码

class Solution {
public:
    int divide(int dividend, int divisor) {
        bool minusOne = dividend >= 0 ? false : true, minusTwo = divisor >= 0 ? false : true;
        long long dividendTemp = dividend, divisorTemp = divisor;
        if (dividendTemp < 0) dividendTemp = -dividendTemp;
        if (divisorTemp < 0) divisorTemp = -divisorTemp;

        long long count = 0;
        while (dividendTemp >= 0) { count++; dividendTemp -= divisorTemp; }

        count--;
        if (minusOne && minusTwo || !minusOne && !minusTwo) {
            if (count >= INT_MAX) return INT_MAX;
            else return count;
        }
        return -count;
    }
};

3. 位操作(Bit Manipulation)

3.1 解题思路

上面的思路中是一个个地减去除数,如果想要节约时间,能不能一次性减去多个除数呢?也就是减去除数的倍数,但是题目要求不能使用乘法、除法和模运算,所以我们可以考虑使用位运算(左位移和右位移):

  1. 整型左位移1位,相当于原数 /= 2;并且移动的时候,最低位舍弃,负数最高位补1,正数最高位补0;
  2. 整型右位移1位,相等于原数 *= 2;并且最低位补0,最高位舍弃;

比如1二进制表示为0001(省略前面的0),左位移一位变成0000 = 0,右位移一位变成0010 = 2。比如-1二进制表示为1111(省略前面的1),左位移一位不变还是1111 = -1,右位移一位变成1110 = -2;

所以我们可以利用左位移的特性,让被除数减去偶数倍的除数,操作步骤如下:

  1. 初始化一个新的被除数(dividendTemp),一个新的除数(divisorTemp ),一个答案(ans),一个倍数(times)。
  2. 并且dividendTemp和divisorTemp 需要绝对值一下,保证计算时都是正数;
  3. 然后判断一下结果是复数还是正数;
  4. 如果除数是1,根据符号直接返回dividendTemp即可;
  5. 接下来,判断一下dividendTemp是不是大于两倍的divisorTemp (divisorTemp << 1),如果是divisorTemp扩大两倍,times也扩大两倍;一直重复这一步直到dividendTemp不在大于两倍的divisorTemp,跳出循环的时候ans += times;
  6. 接下来,判断一下dividendTemp是不是大于等于divisorTemp。如果是只需要ans += 1即可,因为这时dividendTemp最多只比divisorTemp大一倍
  7. 当dividendTemp < divisorTemp,已经做完除法,根据符号返回ans即可;

举个例子:dividendTemp = 14,divisorTemp = 3。14大于两倍的3(= 6),也大于两倍的6(= 12),所以14 - 12 = 2 < 3,循环结束,我们返回4即可。

其实这个思路可以理解成用除数的偶数倍来做除法,最后返回的答案就是除数是被除数的多少倍。当我们保证被除数不大于两倍的除数,这时被除数要么小于除数,直接舍去结果即可(因为整型计算,没有小数);被除数要么等于大于一倍的除数,小于两倍的除数,这时结果再加一即为最终结果。

3.2 实例代码

3.2.1 迭代实现

class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend == INT_MIN && divisor == -1) return INT_MAX;
        long dividendTemp = labs(dividend), divisorTemp = labs(divisor), ans = 0;
        bool ifPositive = (dividend < 0) ^ (divisor < 0) ? false : true; // 结果是正数还是负数,异或运算:相同为0,不同为1
        if (divisorTemp == 1) return ifPositive ? dividendTemp : -dividendTemp;
        while (dividendTemp >= divisorTemp) {
            long divisorMultiply = divisorTemp, times = 1;
            while (dividendTemp >= divisorMultiply << 1) {
                divisorMultiply <<= 1;
                times <<= 1;
            }

            ans += times;
            dividendTemp -= divisorMultiply;
        }

        return ifPositive ? ans : -ans;
    }
};

3.2.2 递归实现

class Solution {
public:
    int divide(int dividend, int divisor) {
        long dividendTemp = labs(dividend), divisorTemp = labs(divisor);
        if (dividendTemp < divisorTemp) return 0;
        long divisorMulply = divisorTemp, times = 1, ans = 0;
        while (dividendTemp > (divisorMulply << 1)) { divisorMulply <<= 1; times <<= 1; }
        ans += times + divide(dividendTemp - divisorMulply, divisorTemp);
        if ((dividend < 0) ^ (divisor < 0)) ans = -ans;
        return ans >= INT_MAX ? INT_MAX : ans;
    }
};

4. 参考资料

  1. [LeetCode] 29. Divide Two Integers 两数相除
  2. C 库函数 - labs()
  3. abs
  4. labs
  5. 负数的位运算问题
  6. 位运算(&、|、^、~、>>、<<)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值