5-2

29. 两数相除

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

返回被除数 dividend 除以除数 divisor 得到的商。

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
示例 2:

输入: dividend = 7, divisor = -3
输出: -2
说明:

被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

My solution (Not Accepted: Time Limit Exceeded):

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        abs_dividend, abs_divisor = abs(dividend), abs(divisor)
        quo = 0
        
        while abs_dividend >= abs_divisor:
            abs_dividend -= abs_divisor
            quo += 1
        
        if quo > pow(2,31) - 1:
            quo = pow(2,31) - 1
            
        if dividend > 0 and divisor < 0 or dividend < 0 and divisor > 0:
            quo = -quo
        
        return quo

不通过的原因:

当被除数特别大而除数又特别小时,while循环会因循环次数过多而超时。

但算法的整体思路是对的,因为不能使用乘除法和mod运算,这个时候就只能通过被除数与除数的加减来实现除法运算。

一种改进方法是加速这个while循环过程,一种很好的思路如下:

class Solution(object):
    def divide(self, dividend, divisor):
        """
        :type dividend: int
        :type divisor: int
        :rtype: int
        """
        positive = (dividend < 0) is (divisor < 0)
        dividend = left = abs(dividend)
        divisor  = div  = abs(divisor)
        Q = 1
        ans = 0
        while left >= divisor:
            left -= div
            ans  += Q 
            Q    += Q
            div  += div
            if left < div:
                div = divisor
                Q = 1
        if not positive:
            return max(-ans, -2147483648)
        else:
            return min(ans, 2147483647)

这个算法的思路是:在每次做完减法运算时,都让div增大一倍(相当于变为原来的两倍),这样随着div不断变大(而且每次翻倍都会变得越来越大),每一轮while循环时dividend被减掉的部分就会越来越多,从而大大加快了整个减法过程的速度。

这里需要注意的是,当div翻倍时,商因子Q也必须要翻倍。

当div大到比dividend剩余的部分还要大时,并不意味着整个减法过程已经结束了,此时让div重新等于最初的divisor,同时Q归1,然后div和Q重新开始新一轮的翻倍过程,直至最终left < divisor。

除此之外,还应注意两点:

(1)对于符号的判断:

positive = (dividend < 0) is (divisor < 0)

这句话很好地将四种情况都包含了进去,例如当dividend > 0且divisor > 0时,上述代码等价于

positive = False is False # output: positive = True

(2)对于溢出情况的处理:

使用max和min函数将输出值截断在\([-2^{31} - 1, 2^{31}]\)之间。

上一篇:位运算之两数相除


下一篇:leetcode-29-两数相除