给定两个整数,被除数 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 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
Related Topics 位运算 数学
思路:
避免直接进行乘法、除法和mod取余,以及正确处理溢出问题
暴力的解法是 以步长为1, 被除数循环减去除数,并记录减去的次数ans,当剩余的数小于除数时,ans即为结果。这种解法的时间复杂度为O(N)。
考虑指数递增步长,翻倍的步长快速逼近结果,相当于对被除数取对数,相应的复杂度为O(log2(N))。
两个数的符号,应该包括:
- 除数被除数同号,结果为正数
- 除数被除数异号,结果为负数
在除数和被除数的绝对值相等时, 以上两个结果的绝对值是相等的。
考虑溢出问题特殊情况:
- 被除数为最小值(左边界)时,除数为1的结果仍为最小值,除数为-1的结果为最大值。
- 除数为最小值时,被除数为最小值的结果为1,被除数为其他值的结果为0。
- 除数为0时,结果为0。
一般情况:被除数和除数的四种符号组合中,他们的结果的绝对值是相等的,因此都可以转为同号处理,又因为转为正正符号时会产生移除情况,而转为负负符号时不会出现溢出情况,因此把四种符号组合都转为负负符号处理。
打表存储指数递增步长,直到步长大于被除数时不再存储,然后逆序遍历步长表,每次结果都增加pow(2,i)。
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
INT_MIN, INT_MAX = -2 ** 31, 2 ** 31 - 1
# 考虑被除数是最小值的情况
if dividend == INT_MIN:
if divisor == -1:
return INT_MAX
if divisor == 1:
return INT_MIN
# 考虑除数是最小值的情况
if divisor == INT_MIN:
return 1 if dividend == INT_MIN else 0
# 考虑除数是0的情况
if divisor == 0:
return 0
# 一般情况
# 将所有符号情况都转为负负符号处理
flag = True # 标记是否是同号
if dividend > 0:
dividend = -dividend
flag = not flag
if divisor > 0:
divisor = -divisor
flag = not flag
# 打表
candidates = [divisor]
while candidates[-1] >= dividend - candidates[-1]:
candidates.append(candidates[-1] + candidates[-1])
print(candidates)
ans = 0
for i in range(len(candidates)-1, -1, -1):
if dividend <= candidates[i]:
ans += (1 << i)
print(ans)
dividend -= candidates[i]
return ans if flag else -ans
s = Solution()
print(s.divide(2147483647, 1))