描述
该题来自于力扣第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
位有符号整数,其数值范围是[−2^31, 2^31 − 1]
。本题中,如果除法结果溢出,则返回2^31 − 1
。
分析
不能使用乘除计算两个数的除法,只能用加减了,考虑到正负只对最后结果的正负号有影响,所以可以先讨论被除数和除数都为正数的情况;而最简单的思路就是用除数循环加自身直到最后的值大于被除数,然后计算循环的次数就是商了。伪代码如下:
1
2
3
4
5sum = divisor
result = 1
while sum < dividend:
sum += divisor
result++;
上面思路显然效率太低了,问题在于每次只加上divisor
,如果每次加上sum
自身,然后result
也加上自身,这样就使得sum = pow(2, result) * divisor
,这样sum
会是指数级增加,比之前的线性增加快了太多;
但是问题来了,按照上面的算法,最终商会定位到pow(2, n-1)
和pow(2, n)
,n
是满足pow(2, n-1) * divisor < dividend < pow(2, n) * divisor
正整数,接下来如何定位到确定的商的,注意到最终的商必然是pow(2, n-1) + k
,即pow(2, n-1) + k = dividend / divisor
,
从而得到k = (dividend - pow(2, n-1) * divisor) / divisor
,就是说k
也是两个数的商,那么可以继续使用上面的思路,只不过这时候被除数变为了dividend - pow(2, n-1) * divisor = dividend - sum
。伪代码也很简单:
1
2
3
4
5
6
7
8
9func div(dividend, divisor):
if (dividend < divisor):
return 0
sum = divisor
result = 1
while (sum + sum) < dividend:
sum += sum
reuslt += result
return result + div(dividend - sum, divisor)
由于sum+sum
可能会溢出,所以改为sum < dividend - sum
就好。
最后无非是考虑边界了,由于int32的数值范围是[-2^31, 2^31-1]
,所以如果把值转为正数,那么范围会变成[0, 2^31]
,这时候2^31
就会溢出,怎么办呢?其实这里有个小技巧,不都转为正数,而是都转为负数,因为转为负数后的范围是[-2^31, 0]
,这样就不会溢出了。当然被除数与除数全部转为负数后,要注意代码中的大小关系与全转为正数时的不同。
当然如果全部使用long long
长整型,就没有那么多边界的问题需要考虑了。但是题目应该是倾向于只使用int
整型。
代码
c++
1 | class Solution { |