题目难度: 简单
休息了一个月, 我又满血回来了, 今天我们来开始一个新的系列 - Leetcode 的剑指 Offer(专项突击版)!!!
它相比剑指 Offer (Easy~Medium) 或者程序员面试金典 (Medium~Hard), 难度都要大一些 (Medium+~Hard), 更适合进阶, 但考虑到现在越来越卷的面试, 推荐程度更高了, 6 颗星!!! ✨✨✨✨✨✨
如果你是萌新, 可以先看看其他两个系列哦, 公众号里回复
剑指offer和面试金典关键字就能看到了
这个系列的更新频率还是老样子: 每周日晚上的 6 点 45 分, 大家可以多多关注哦
另外大家在我的公众号"算法精选"中的聊天框中回复
剑指offer2就能看到新系列当前已经更新的文章了
题目描述
- 给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。
- 注意:
- 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31−1]。本题中,如果除法结果溢出,则返回 2^31 − 1
示例 1:
- 输入:a = 15, b = 2
- 输出:7
- 解释:15/2 = truncate(7.5) = 7
示例 2:
- 输入:a = 7, b = -3
- 输出:-2
- 解释:7/-3 = truncate(-2.33333…) = -2
示例 3:
- 输入:a = 0, b = 1
- 输出:0
示例 4:
- 输入:a = 1, b = 1
- 输出:1
提示:
- -2^31 <= a, b <= 2^31 - 1
- b != 0
题目思考
- 除法的本质是什么?
- 可以利用哪些其他操作符?
解决方案
思路
- 分析题目, 一个直观的思路就是用减法代替除法: 循环减去除数并计数, 直到被除数小于除数为止, 最终计数就是商
- 但这种方法复杂度跟商的大小成正比, 假设被除数是 2^31, 除数是 2, 那就要循环 2^30 次, 效率过低, 有没有其他更高效的做法呢?
- 回顾题目要求, 它只是禁用了乘除和取模, 那除了四则运算, 我们程序员经常用到的还有位运算符, 以及它们的好伙伴: 二进制
- 回想二进制的性质, 我们的商转成二进制的话就相当于若干个位是 1, 然后其余位是 0
- 那么商就可以表示为多个 2 的幂的和, 例如
12(0b1100), 就相当于8(0b1000)+4(0b0100) - 这样我们在进行除法的时候, 就可以不断循环, 进而将商分解, 例如
128/3可以分解为3*(32+8+2), 即3*42 - 具体做法就是初始化当前 2 的幂为 1, 并将它与除数的乘积初始化为除数, 然后将 2 的幂和除数乘积不断左移, 模拟乘 2, 直到除数乘积最接近且小于等于被除数为止
- 此时得到的 2 的幂就可以累加到最终的商里面了, 然后将被除数减去得到的除数乘积, 继续开启新一轮的循环, 直到最终被除数小于原始除数为止
- 另外根据题目要求, 还需要额外处理正负号以及数字范围
- 下面代码中对上面提到的每个步骤都有详细注释, 方便大家理解
复杂度
- 时间复杂度 O(logA): 每次循环只需要 A 的对数时间, 然后只会循环常数次
- 空间复杂度 O(1): 只使用了几个常数空间的变量
代码
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
# 核心思路: 2的幂和除数乘积不断左移一位, 直到最接近且小于等于被除数, 循环该过程直到被除数<除数
# 先将数字都转为正数, 并记录商的符号
neg = False
if dividend < 0:
dividend = -dividend
neg = not neg
if divisor < 0:
divisor = -divisor
neg = not neg
res = 0
# 注意循环条件是大于等于, 因为等于情况下仍有商, 为1
while dividend >= divisor:
# 2的幂初始化为1
mi = 1
# 除数乘积初始化为除数
cur = divisor
# 注意循环条件是大于等于, 相等时表示恰好整除
while dividend >= (cur << 1):
# 2的幂和除数乘积不断左移一位, 直到最接近且小于等于被除数
cur <<= 1
mi <<= 1
# 此时的除数乘积就是最接近且小于等于被除数的数了
# 商加上当前的2的幂
res += mi
# 被除数减去已经使用的除数乘积
dividend -= cur
# 恢复符号
if neg:
# 负号时需要取相反数
res = -res
# 注意溢出时取最大正数
mn, mx = -(2**31), 2**31 - 1
if res < mn or res > mx:
return mx
return res
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家关注~😊

949

被折叠的 条评论
为什么被折叠?



