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}]\)之间。