难度:medium
本题同时也是《剑指offer 专项突击》的第一题。
Problem
https://leetcode.cn/problems/divide-two-integers/
以及
https://leetcode.cn/problems/xoh6Oh/
给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。
注意:
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335)
= -2 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 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
提示:
-231 <= a, b <= 231 - 1 b != 0
没有灵魂的解法
最直观的想法 是将除法转为暴力减法, 直到减到 小于除数为止。复杂度是 O ( n ) \mathcal{O}(n) O(n),当除数很小,比如为1时,效率太低。
a=abs(a)
b=abs(b)
res = 0
while a>=b:
a-=b
res+=1
优化后的解法
先说有哪些坑:
- 溢出问题。(也看到有C/C++解法是将 int 转为 long)注意到如果全部转为正数做,会面临 − 2 31 -2^{31} −231=> 转为 2 31 2^{31} 231溢出的情况。
示例5:
a=-2147483648
b=-1
所以应该先把除数和被除数都转为负数,运算后再考虑符号问题。但是只有一种情况会溢出,也就是输入为 ( − 2 31 ) / ( − 1 ) = 2 31 (-2^{31})/(-1)=2^{31} (−231)/(−1)=231时会溢出。这种case写个if排除就行了。
INT_MIN, INT_MAX = -2**31, 2**31 - 1
assert b!=0
if a==INT_MIN and b==-1:
return INT_MAX
对于其他case的符号判断, 由于不允许使用符号*, /, %
。可以通过最高位判断符号,一个异或搞定。
if (a ^ b) <0:
sign=-1
else:
sign =1
- 计算复杂度的问题。有不少解法(包括《剑指offer 专项》书上的解法)都是优化为考虑 减去除数的倍数,这种解法在python下可能还是超出时间(自己测试不会), 复杂度是
O
(
l
o
g
n
l
o
g
n
)
O(log n \ \ log n )
O(logn logn)。
思路大概是这样的:
完整代码(思路:减去除数2^k倍)
class Solution:
def divide(self, a: int, b: int) -> int:
INT_MIN, INT_MAX = -2**31, 2**31 - 1
assert b!=0
if a==INT_MIN and b==-1:
return INT_MAX
if (a ^ b) <0:
sign=-1
else:
sign =1
a=abs(a)
b=abs(b)
K=0
while a>=b:
value, k = b,1
while a-value-value>=b:
value+=value
k+=k
a-=value
K+=k
if sign==-1:
K = -K
return K