位运算题目:两数相除

题目

标题和出处

标题:两数相除

出处: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, 2311]。对于这道题,如果商严格大于 2 31 − 1 \texttt{2}^\texttt{31} - 1 2311 则返回 2 31 − 1 \texttt{2}^\texttt{31} - 1 2311,如果商严格小于 -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} -231dividend, divisor2311
  • 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 2311,最小值是 − 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 2311

以下三种情况可以直接返回结果,优先处理。

  • 如果 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 2311

其余情况,为了方便处理,首先根据被除数和除数的正负性判断商的正负性,然后将被除数和除数都变成负数,计算商的绝对值。将被除数和除数都变成负数是为了可以处理 − 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

  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 的最小值(对应绝对值最大)。

  2. 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

  3. 由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 1 1 1 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividendcurrDivisor,此时 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

  4. 由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 3 3 3 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividendcurrDivisor,此时 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

  5. 由于 dividend > currDivisor \textit{dividend} > \textit{currDivisor} dividend>currDivisor,因此将 currDivisor \textit{currDivisor} currDivisor 右移 1 1 1 位,使得 dividend ≤ currDivisor \textit{dividend} \le \textit{currDivisor} dividendcurrDivisor,此时 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

  6. 此时 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(logdividend),其中 dividend \textit{dividend} dividend 是给定的被除数。计算 currDivisor \textit{currDivisor} currDivisor 的过程中,左移操作的次数是 O ( log ⁡ ∣ dividend ∣ ) O(\log |\textit{dividend}|) O(logdividend) 次,计算商的过程中需要将 currDivisor \textit{currDivisor} currDivisor 右移,每次右移之后最多更新结果一次,因此时间复杂度是 O ( log ⁡ ∣ dividend ∣ ) O(\log |\textit{dividend}|) O(logdividend)

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值