leetcode-两数相除

253 阅读3分钟

这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战

今天是中国医师节,如果不是因为老婆是医生,朋友圈有很多她同事发的照片,我想我也很难注意到这个不太知名的节日。

医生本来应该是令人尊敬的职业,特别是在处于疫情防控的当下,医生被社会的需要程度是比之前更多的。即使是这样,中国医师节这个节日还是知道的人非常少,引用一段话,希望更多人知道这个节日:

2017年11月3日,国务院通过了卫计委关于“设立中国医师节”的申请,同意自2018年起,将每年的8月19日设立为“中国医师节”。中国医师节是经国务院同意设立的卫生与健康工作者的节日,体现了党和国家对1100多万卫生与健康工作者的关怀和肯定。

说回来,技术方面我们需要继续精进,今天做的是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

思路

这个题目一看挺简单,但是有意思的是,不让使用乘法、除法和 mod 运算符。
除法不让用,不能直接求的答案。我们就需要想另外的办法来逼近答案,一个重要的线索是,结果是正数,那么我们是可以不断逼近来求的,如果是小数,可以逼近但是可能永远也无法得到最终结果。
乘法不让使用,我们知道可以使用位移来替代 *2 或者 /2 这个操作。
有了上面这些线索,我们可以这么做:
1、先处理掉特殊情况,比如 dividend = 0, divisor = 1, divisor = -1。特别是 divisor = -1的时候,还需要考虑溢出,因为最小的负数是 -2^31, 最大的正数是 2^31-1
2、处理掉特殊情况后,还要处理掉结果的正负号
3、都处理完之后,我们就变成比较简单的 a / b的情况了,a < b,那么返回0,否则,结果大于等于1。如果a >= b + b,那么结果可以 * 2,因为不能用乘法,这个操作可以用上面说的位移操作来代替。b = b * 2,然后继续比较,直到a比b小为止。

有一个注意点是,在计算的过程中,可能超过int的最大值,比如上面说到的 *2 的操作,如果原始数据是 2^30,那么乘以2就会溢出,所以比较简单的处理方式是直接使用long。

Java版本代码

class Solution {
    public int divide(int dividend, int divisor) {
        if (dividend == 0) {
            return 0;
        }
        if (divisor == 1) {
            return dividend;
        }
        if (divisor == -1) {
            // 注意取值范围为 -2^31 ~ 2^31 -1 ,所以当-2^31 / -1 时,结果会溢出
            if (dividend == Integer.MIN_VALUE) {
                return Integer.MAX_VALUE;
            } else {
                return -dividend;
            }
        }
        // 判断结果的正负
        boolean neg = false;
        long dividendLong = dividend;
        if (dividend < 0) {
            neg = !neg;
            dividendLong = 0L - dividend;
        }
        long divisorLong = divisor;
        if (divisor < 0) {
            neg = !neg;
            divisorLong = 0L - divisor;
        }
        int ans = divideSimple(dividendLong, divisorLong);
        if (neg) {
            ans = -ans;
        }
        return ans;
    }

    /**
     * 简单除法,把各种异常的情况都已经排除,dividend和divisor都是非负整数,而且结果也不会超过限制
     * @param dividend
     * @param divisor
     * @return
     */
    private static int divideSimple(long dividend, long divisor) {
        if (dividend < divisor) {
            return 0;
        }
        int ans = 1;
        long divisorTemp = divisor;
        while (dividend >= divisorTemp + divisorTemp) {
            ans = ans << 1;
            divisorTemp = divisorTemp << 1;

        }
        return ans + divideSimple(dividend - divisorTemp, divisor);
    }
}