1. 程式人生 > 其它 >[LeetCode 29.] 兩數相除

[LeetCode 29.] 兩數相除

LeetCode 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

解題思路

不讓用除法和取餘,那就只能用減法來模擬除法了。
顯然,暴力減法是有問題的,對於 INT32_MAX / 1 這種場景會超時。這裡我們必然要想辦法加倍減。
既然題目告訴我們不能用乘法操作,那我們就用位移操作來代替乘法。也就是對 dd / dr = k 按照 k 進行二進位制分解運算,也就是說 dd = dr * k = sum(dri * 2^i),按照這種方式逆運算出 k 每一位上的數字即可。
思路如下:

  • 首先將除數和被除數對齊,也就是找到一個數,這個數字是被除數的2的冪次倍,且恰好比被除數小;
  • 然後從這個數開始,每一輪迴圈減去此數,直到被除數比此數小,此時結果需要累加的就是減法次數乘上此數相對於除數的倍數;
  • 之後此數右移一位,也就是降為原來的1/2,重複上一步,直到數字比原本的除數要小,結束演算法。

參考程式碼

class Solution {
public:
    int divide(int dividend, int divisor) {
        assert(divisor != 0);
        int flag = 1;
        if (dividend > 0 && divisor < 0 || dividend < 0 && divisor > 0) flag = -1;
        int64_t dd = llabs(dividend);
        int64_t dr = llabs(divisor);
        if (dd < dr) return 0;
        if (dd == dr) return flag;

        int64_t dri = dr;
        int64_t base = 1;
        while (dd >= dri) {
            dri <<= 1;
            base <<= 1;
        }
        dri >>= 1;
        base >>= 1;

        int64_t res = 0;
        while (dd >= dr && dri >= dr) {
            while (dd >= dri) {
                dd -= dri;
                res += base;
            }
            dri >>= 1;
            base >>= 1;
        }
        res *= flag;
        if (res < INT32_MIN || res > INT32_MAX) return INT32_MAX;
        return res;
    } // AC
};