1. 程式人生 > >LeetCode 29. 兩數相除

LeetCode 29. 兩數相除

題目描述

給定兩個整數,被除數 dividend 和除數 divisor。將兩數相除,要求不使用乘法、除法和 mod 運算子。
返回被除數 dividend 除以除數 divisor 得到的商。

示例 1:

輸入: dividend = 10, divisor = 3
輸出: 3

示例 2:

輸入: dividend = 7, divisor = -3
輸出: -2

說明:

  • 被除數和除數均為 32 位有符號整數。
  • 除數不為 0。
  • 假設我們的環境只能儲存 32 位有符號整數,其數值範圍是 [−231, 231 − 1]。本題中,如果除法結果溢位,則返回 231
    − 1。

思路

  1. 需要判斷結果的符號

    • 在數的二進位制表示中,首位為1表示負數,0表示正數;
    • 那麼,通過兩數異或 (^) 操作,即可知道結果是否為正:負數 ^ 正數 = 負數
  2. 初始的時候,需要將被除數和除數都取絕對值,得到tmp_dividendtmp_divisor

    • 這裡我們要用到位運算實現的加法運算;
    • 取相反數的操作為:按位取反再加1,即 add(~x, 1)
    • 需要注意的是,定義這兩個變數的型別為long long,因為該題的測試用例中包含了INT_MININT_MAX的情況
    • 如果輸入為-2147483648,並且定義的變數型別為int,那麼得到的結果與預期不符,原因在於-2147483648
      相反數為2147483648 > INT_MAX產生了溢位 (哭暈在廁所,這道題的case真的很嚴格(ಥ_ಥ))
  3. 主要思路就是用被除數減去除數,用變數res儲存解,也就是減去了多少個除數

    • 數m左移n位,結果為m × 2n
    • 為了加快速度,可以每次減去除數x2,通過左移 (<<) 運算實現: tmp << 1
    • 內層迴圈用來將tmp不斷逼近tmp_dividend,當tmp_dividend-tmp>=0時,說明tmp的移位操作還沒超過tmp_dividend並且將tmp_dividend更新為當前的被除數tmp_dividendtmp的差; 變數res初始化為1,那麼每當迴圈執行了一次之後,相應的,res << 1
      (除數左移1次,res也左移一次)
    • 如果左移一位的除數過大,則退出內層迴圈,除數tmp還原為一開始的tmp_divisor
    • 外層迴圈中,當tmp_dividend >= tmp_divisor,說明當前的被除數tmp_dividend仍包含至少一個除數tmp_divisor,可以繼續
    • 比如:7 / 3,3 << 1之後為6,res << 1為2,相當於此時減去的是兩個3
  4. 因此總結起來就是兩層while迴圈,內層迴圈用來試探不斷逼近tmp_dividend,當用來試探的tmp超過了tmp_dividend時,退出內層迴圈,當前被除數重新賦值為一開始的tmp_divisor

程式碼


long long add(long long a, long long b)
{
    long long sum = a;
    long long carry = b;

    while (carry)
    {
        long long tmps = sum;

        sum = tmps ^ carry;
        carry = (tmps & carry) << 1;
    }

    return sum;
}


int divide(int dividend, int divisor) {
    // 取絕對值
    long long tmp_dividend = dividend < 0 ? add(~dividend, 1) : dividend; 
    long long tmp_divisor = divisor < 0 ? add(~divisor, 1) : divisor;

    long long res = 0;
    while (tmp_dividend >= tmp_divisor){
        long long tmp = tmp_divisor;
        int i = 1;

        while (tmp_dividend - tmp >= 0){
            tmp_dividend = tmp_dividend - tmp;
            res += i;
            i = i << 1;
            tmp = tmp << 1;
        }
    }

    // 如果商小於0,返回res的相反數
    if ((dividend ^ divisor) < 0)
    {
        res = add(~res, 1);

    }

    res = res > INT_MIN ? res : INT_MIN;

    return res < INT_MAX ? res : INT_MAX;


}