LeetCode 29. 兩數相除
阿新 • • 發佈:2019-01-26
題目描述
給定兩個整數,被除數 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表示負數,0表示正數;
- 那麼,通過兩數異或 (^) 操作,即可知道結果是否為正:負數 ^ 正數 = 負數
初始的時候,需要將被除數和除數都取絕對值,得到
tmp_dividend
和tmp_divisor
- 這裡我們要用到位運算實現的加法運算;
- 取相反數的操作為:按位取反再加1,即
add(~x, 1)
- 需要注意的是,定義這兩個變數的型別為
long long
,因為該題的測試用例中包含了INT_MIN
與INT_MAX
的情況 - 如果輸入為
-2147483648
,並且定義的變數型別為int
,那麼得到的結果與預期不符,原因在於-2147483648
2147483648
>INT_MAX
產生了溢位 (哭暈在廁所,這道題的case真的很嚴格(ಥ_ಥ))
主要思路就是用被除數減去除數,用變數
res
儲存解,也就是減去了多少個除數- 數m左移n位,結果為m × 2n
- 為了加快速度,可以每次減去除數x2,通過左移 (<<) 運算實現:
tmp << 1
- 內層迴圈用來將tmp不斷逼近
tmp_dividend
,當tmp_dividend-tmp>=0
時,說明tmp
的移位操作還沒超過tmp_dividend
,並且將tmp_dividend
更新為當前的被除數tmp_dividend
與tmp
的差; 變數res
初始化為1,那麼每當迴圈執行了一次之後,相應的,res << 1
- 如果左移一位的除數過大,則退出內層迴圈,除數
tmp
還原為一開始的tmp_divisor
- 外層迴圈中,當
tmp_dividend >= tmp_divisor
,說明當前的被除數tmp_dividend
仍包含至少一個除數tmp_divisor
,可以繼續 - 比如:7 / 3,3 << 1之後為6,res << 1為2,相當於此時減去的是兩個3
因此總結起來就是兩層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;
}