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
提示:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
解题思路
思路:递归
本篇使用的是递归的方法, 这里递归能够执行下去,而不会因为递归层数抛出异常的原因是因为内部先进行翻倍计算,多次翻倍结果若大于被除数,则进行下一层递归,具体可看下面代码实现部分。
算法:
- 处理特殊情况:1. 被除数为 0,返回 0;2. 除数为 1,返回被除数;
- 计算时,将被除数和除数都转换为正数进行计算,(用 sign 标记原被除数和除数的商的符号,在最终返回结果再进行判定)
- 边界情况处理:
- 商为正数的情况下:这里要区分,如果商大于 INT_MAX,返回 INT_MAX;否则返回 ans。(这里是因为只有同符号且被除数和除数都为负号时,可能会溢出)
- 商为负数的情况下:这里直接返回 -ans(上面已经提及,计算是将被除数和除数都转换为正数)
- 做除法:
- 其实里面主要转换为加法和减法;
- count 统计次数,除数翻倍累加,当累加后的除数比被除数还大时,这个时候,被除数减去上一次累加的除数,再次与原除数翻倍累加的结果比较;
- 具体看下面的例子:
假设 dividend = 10,divisor = 3:
前提条件: dividend > divisor
设 count = 1 开始统计,复制除数给另外一个变量同时进行翻倍累加,由 3 变为 6,这里 6 比被除数 dividend 小,那么 count 也可进行翻倍累加变为 2;
重复,这个时候 6 翻倍变为 12,12 比 dividend 大,所以这次累加不能够算入其中。所以用被除数减去上一次除数累加的结果,也就是 6,剩 4,进入下一次递归;
这时候相当于 dividend 变为 4,原除数还为 3,符合前提条件。重复上面的操作,因为除数累加结果 6 还是比 4 大,所以这个时候,还是用被除数减去除数,其中 count 未变化为 1。
此时,dividend 为 1,divisor 为 3,不符合前提条件,直接返回 0。count 在递归逐层返回累加结果,最终值为 2 + 1 = 3。
这部分代码实现如下(m:表示 dividend;n:表示 divisor):
def div(m, n):
if m < n:
return 0
# 成倍计算,
count = 1
# n 要用于下次递归
# 这里 t_n 用以翻倍计算
t_n = n
while (t_n + t_n) <= m:
count += count
t_n += t_n
# 成倍计算超出 m 时
# 剩下部分用以下次递归计算
return count + div(m - t_n, n)
完整的代码如下。
代码实现
# -*- coding: utf-8 -*-
"""
@Time: 2020/4/13 17:46
@File: divide_two_integers.py
@Author: Demon
@Contact: yiluolion@126.com
"""
# put the import lib here
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
def div(m, n):
if m < n:
return 0
# 成倍计算,
count = 1
# n 要用于下次递归
# 这里 t_n 用以翻倍计算
t_n = n
while (t_n + t_n) <= m:
count += count
t_n += t_n
# 成倍计算超出 m 时
# 剩下部分用以下次递归计算
return count + div(m - t_n, n)
# 处理特殊情况
if dividend == 0:
return 0
if divisor == 1:
return dividend
# 边界值
INT_MAX = (2 << 30) - 1
INT_MIN = -2 << 30
sign = 1
if (divisor > 0 and dividend < 0) or (divisor < 0 and dividend > 0):
sign = -1
m = dividend if dividend > 0 else -dividend
n = divisor if divisor > 0 else -divisor
ans = div(m, n)
# 这里表示 dividend 和 divisor 为同符号的情况
# 但是若是同为负数的情况下,可能会出现边界问题
# 所以这里需要判断结果是否溢出
if sign > 0:
return ans if ans < INT_MAX else INT_MAX
return -ans
# dividend = 10
# divisor = 3
# dividend = 7
# divisor = -3
# s = Solution()
# s.divide(dividend, divisor)
实现结果
以上就是使用递归的方法,将除法拆分为加法、减法,用以解决《29. 两数相除》问题的主要内容。
欢迎关注微信公众号《书所集录》
共同学习,写下你的评论
评论加载中...
作者其他优质文章