位运算题目:两数相除
文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:两数相除
出处:29. 两数相除
难度
6 级
题目描述
要求
给定两个整数,被除数 dividend \texttt{dividend} dividend 和除数 divisor \texttt{divisor} divisor,要求不使用乘法、除法和取余运算符,计算两个整数的除法。
整数除法的结果应当向零取整,即去掉分数部分。例如, 8.345 \texttt{8.345} 8.345 取整得到 8 \texttt{8} 8, -2.7335 \texttt{-2.7335} -2.7335 取整得到 -2 \texttt{-2} -2。
注意:假设我们的环境只能存储 32 \texttt{32} 32 位有符号整数,其数值范围是 [−2 31 , 2 31 − 1] \texttt{[−2}^\texttt{31}\texttt{, 2}^\texttt{31} − \texttt{1]} [−231, 231−1]。对于这道题,如果商严格大于 2 31 − 1 \texttt{2}^\texttt{31} - 1 231−1 则返回 2 31 − 1 \texttt{2}^\texttt{31} - 1 231−1,如果商严格小于 -2 31 \texttt{-2}^\texttt{31} -231 则返回 -2 31 \texttt{-2}^\texttt{31} -231。
示例
示例 1:
输入: dividend = 10, divisor = 3 \texttt{dividend = 10, divisor = 3} dividend = 10, divisor = 3
输出: 3 \texttt{3} 3
解释: 10/3 = 3.33333.. \texttt{10/3} = \texttt{3.33333..} 10/3=3.33333..,取整得到 3 \texttt{3} 3。
示例 2:
输入: dividend = 7, divisor = -3 \texttt{dividend = 7, divisor = -3} dividend = 7, divisor = -3
输出: -2 \texttt{-2} -2
解释: 7/-3 = -2.33333.. \texttt{7/-3} = \texttt{-2.33333..} 7/-3=-2.33333..,取整得到 -2 \texttt{-2} -2。
数据范围
- -2 31 ≤ dividend, divisor ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{dividend, divisor} \le \texttt{2}^\texttt{31} - \texttt{1} -231≤dividend, divisor≤231−1
- divisor ≠ 0 \texttt{divisor} \ne \texttt{0} divisor=0
解法
思路和算法
由于题目要求给定的环境只能存储 32 32 32 位有符号整数,因此不考虑使用 long \texttt{long} long 型的解法。虽然使用 long \texttt{long} long 型可以简化实现,但是不符合题目要求。
由于被除数 dividend \textit{dividend} dividend 的最大值是 2 31 − 1 2^{31} - 1 231−1,最小值是 − 2 31 -2^{31} −231,因此商不在 32 32 32 位有符号整数范围中的情况只有 dividend = − 2 31 \textit{dividend} = -2^{31} dividend=−231 且 divisor = − 1 \textit{divisor} = -1 divisor=−1 的情况,此时商是 2 31 2^{31} 231,超出 32 32 32 位有符号整数范围,应返回 2 31 − 1 2^{31} - 1 231−1。
以下三种情况可以直接返回结果,优先处理。
-
如果 dividend = 0 \textit{dividend} = 0 dividend=0,则商是 0 0 0,返回 0 0 0。
-
如果 divisor = 1 \textit{divisor} = 1 divisor=1,则商与被除数相等,返回 dividend \textit{dividend} dividend。
-
如果 dividend = − 2 31 \textit{dividend} = -2^{31} dividend=−231 且 divisor = − 1 \textit{divisor} = -1 divisor=−1,则商是 2 31 2^{31} 231,超出 32 32 32 位有符号整数范围,因此返回 2 31 − 1 2^{31} - 1 231−1。
其余情况,为了方便处理,首先根据被除数和除数的正负性判断商的正负性,然后将被除数和除数都变成负数,计算商的绝对值。将被除数和除数都变成负数是为了可以处理 − 2 31 -2^{31} −231 的情况。
由于题目要求不能使用乘法和除法,因此需要使用移位运算代替乘法和除法。
首先将除数左移,使得在除数的绝对值不超过被除数的绝对值的情况下,将除数的绝对值最大化。假设除数左移了 k k k 位之后变成 currDivisor \textit{currDivisor} currDivisor,则 currDivisor = divisor × 2 k \textit{currDivisor} = \textit{divisor} \times 2^k currDivisor=divisor×2k, currDivisor \textit{currDivisor} currDivisor 除以 divisor \textit{divisor} divisor 的商是 2 k 2^k 2k。因此将结果增加 2 k 2^k 2k,将 dividend \textit{dividend} dividend 的值减去 currDivisor \textit{currDivisor} currDivisor,然后对剩余的 dividend \textit{dividend} dividend 继续计算商。
当 dividend \textit{dividend} dividend 的值减去 currDivisor \textit{currDivisor} currDivisor 之后,如果 dividend \textit{dividend} dividend 的绝对值小于 currDivisor \textit{currDivisor} currDivisor 的绝对值,则需要将 currDivisor \textit{currDivisor} currDivisor 右移,直到 dividend \textit{dividend} dividend 的绝对值大于等于 currDivisor \textit{currDivisor} currDivisor 的绝对值,然后重复上述操作,计算两数相除的结果。当 dividend \textit{dividend} dividend 的绝对值小于 divisor \textit{divisor} divisor 的绝对值时,商的剩余部分取整的结果是零,因此结束计算,此时即可得到两数相除的结果的绝对值,根据事先判断的商的正负性得到两数相除的结果。
需要注意的是,由于上述计算过程比较的是绝对值,而 dividend \textit{dividend} dividend、 divisor \textit{divisor} divisor 和 currDivisor \textit{currDivisor} currDivisor 都是负数,因此比较两数之间关系的不等号方向与比较两数绝对值之间关系的不等号方向相反。
以下用一个例子说明计算过程。被除数 dividend \textit{dividend} dividend 是 − 512 -512 −512,除数 divisor \textit{divisor} divisor 是 − 5 -5 −5。用 quotient \textit{quotient} quotient 表示两数相除的结果的绝对值,用 factor \textit{factor} factor 表示将 divisor \textit{divisor} divisor 左移之后对应的因数,初始时 quotient = 0 \textit{quotient} = 0 quotient=0, factor = 1 \textit{factor} = 1 factor=1。
-
将 divisor \textit{divisor} divisor 左移 6 6 6 位,得到 currDivisor = factor × 2 6 = − 5 × 64 = − 320 \textit{currDivisor} = \textit{factor} \times 2^6 = -5 \times 64 = -320 currDivisor=factor×26=−5×64=−320, factor = 2 6 = 64 \textit{factor} = 2^6 = 64 factor=26=64。此时 currDivisor \textit{currDivisor} currDivisor 是将 factor \textit{factor} factor 左移之后的不小于 dividend \textit{dividend} dividend 的最小值(对应绝对值最大)。
-
将 quotient \textit{quotient} quotient 的值增加 factor \textit{factor} factor,将 dividend \textit{dividend} dividend 的值减少 currDivisor \textit{currDivisor} currDivisor。此时 quotient = 64 \textit{quotient} = 64 quotient=64, dividend = − 192 \textit{dividend} = -192 dividend=−192。
-
由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 1 1 1 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividend≤currDivisor,此时 currDivisor = − 160 \textit{currDivisor} = -160 currDivisor=−160, factor = 32 \textit{factor} = 32 factor=32。将 quotient \textit{quotient} quotient 的值增加 factor \textit{factor} factor,将 dividend \textit{dividend} dividend 的值减少 currDivisor \textit{currDivisor} currDivisor。此时 quotient = 96 \textit{quotient} = 96 quotient=96, dividend = − 32 \textit{dividend} = -32 dividend=−32。
-
由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 3 3 3 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividend≤currDivisor,此时 currDivisor = − 20 \textit{currDivisor} = -20 currDivisor=−20, factor = 4 \textit{factor} = 4 factor=4。将 quotient \textit{quotient} quotient 的值增加 factor \textit{factor} factor,将 dividend \textit{dividend} dividend 的值减少 currDivisor \textit{currDivisor} currDivisor。此时 quotient = 100 \textit{quotient} = 100 quotient=100, dividend = − 12 \textit{dividend} = -12 dividend=−12。
-
由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 1 1 1 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividend≤currDivisor,此时 currDivisor = − 10 \textit{currDivisor} = -10 currDivisor=−10, factor = 2 \textit{factor} = 2 factor=2。将 quotient \textit{quotient} quotient 的值增加 factor \textit{factor} factor,将 dividend \textit{dividend} dividend 的值减少 currDivisor \textit{currDivisor} currDivisor。此时 quotient = 102 \textit{quotient} = 102 quotient=102, dividend = − 2 \textit{dividend} = -2 dividend=−2。
-
此时 dividend > divisor \textit{dividend} > \textit{divisor} dividend>divisor,即 dividend \textit{dividend} dividend 的绝对值小于 divisor \textit{divisor} divisor 的绝对值,结束计算。由于初始时 dividend \textit{dividend} dividend 和 divisor \textit{divisor} divisor 的符号相同,因此结果是正数,返回 quotient = 102 \textit{quotient} = 102 quotient=102。
代码
class Solution {public int divide(int dividend, int divisor) {if (dividend == 0) {return 0;}if (divisor == 1) {return dividend;}if (dividend == Integer.MIN_VALUE && divisor == -1) {return Integer.MAX_VALUE;}boolean negative = dividend < 0 ^ divisor < 0;dividend = -Math.abs(dividend);divisor = -Math.abs(divisor);int quotient = 0;int currDivisor = divisor, factor = 1;while (dividend >> 1 < currDivisor) {currDivisor <<= 1;factor <<= 1;}while (dividend <= divisor) {while (dividend > currDivisor) {currDivisor >>= 1;factor >>= 1;}quotient += factor;dividend -= currDivisor;}if (negative) {quotient = -quotient;}return quotient;}
}
复杂度分析
-
时间复杂度: O ( log ∣ dividend ∣ ) O(\log |\textit{dividend}|) O(log∣dividend∣),其中 dividend \textit{dividend} dividend 是给定的被除数。计算 currDivisor \textit{currDivisor} currDivisor 的过程中,左移操作的次数是 O ( log ∣ dividend ∣ ) O(\log |\textit{dividend}|) O(log∣dividend∣) 次,计算商的过程中需要将 currDivisor \textit{currDivisor} currDivisor 右移,每次右移之后最多更新结果一次,因此时间复杂度是 O ( log ∣ dividend ∣ ) O(\log |\textit{dividend}|) O(log∣dividend∣)。
-
空间复杂度: O ( 1 ) O(1) O(1)。