題目名,兩數相除,表面看起來人畜無害。結果…,他喵的提交十幾次才過。
題目描述(題目難度,中等)
給定兩個整數,被除數 dividend
和除數 divisor
。將兩數相除,要求不使用乘法、除法和 mod 運算符。
返回被除數 dividend
除以除數 divisor
得到的商。
示例 1:
輸入: dividend = 10, divisor = 3
輸出: 3
示例 2:
輸入: dividend = 7, divisor = -3
輸出: -2
說明:
- 被除數和除數均爲 32 位有符號整數。
- 除數不爲 0。
- 假設我們的環境只能存儲 32 位有符號整數,其數值範圍是 [, ]。本題中,如果除法結果溢出,則返回 。
題目求解
解法一
最開始看題目也沒什麼時間複雜度的要求,以爲題目難點主要在於不能使用乘除和取餘運算。於是想到一個直白的試減法就提交了,試減法的思路就是每次循環都將被除數減去一個除數,這樣最終被除數被減去的次數就是商。
其實這個解法在英文版官網上是可以提交通過的,這樣也比較符合劃定的中等難度的級別。但到了中文版,就會說超出時間限制,無法通過。所以沒辦法,只能優化了這個解法,最終在中文版網站提交通過。
這個試減法,我是將輸入的被除數和除數統一轉換爲負數,再進行試減的。爲什麼這樣呢,我們平常不是習慣於正數的計算嗎。其實我最開始也是統一轉化爲正數來做的,但這樣做有一個問題就是,如果被除數爲 Integer.MIN_VALUE
,則它無法轉化爲對應的正數。需要注意:Integer.MIN_VALUE == -Integer.MIN_VALUE
。
class Solution {
public int divide(int dividend, int divisor) {
if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 題意
if(divisor == Integer.MIN_VALUE){ // **此處如不特判,後面會陷入死循環
if(dividend == Integer.MIN_VALUE) return 1;
else return 0;
}
if(divisor == -1) return -dividend;
if(divisor == 1) return dividend;
boolean d = true; // 同號標誌
if(dividend <= 0 && divisor > 0){
d = false;
divisor = -divisor;
}else if(dividend >= 0 && divisor < 0){
d = false;
dividend = -dividend;
}else if(dividend >= 0 && divisor > 0){
dividend = -dividend;
divisor = -divisor;
}
int result = 0;
while(true){
dividend -= divisor;
if(dividend > 0) return d == true ? result : -result;
++result;
}
}
}
解法二
加速的試減法,使用移位運算來加速試減。
class Solution {
public int divide(int dividend, int divisor) {
if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 題意
if(divisor == Integer.MIN_VALUE){ // **此處如不特判,後面會陷入死循環
if(dividend == Integer.MIN_VALUE) return 1;
else return 0;
}
if(divisor == -1) return -dividend;
if(divisor == 1) return dividend;
boolean d = true; // 同號標誌
if(dividend <= 0 && divisor > 0){
d = false;
divisor = -divisor;
}else if(dividend >= 0 && divisor < 0){
d = false;
dividend = -dividend;
}else if(dividend >= 0 && divisor > 0){
dividend = -dividend;
divisor = -divisor;
}
int div = divisor, k = 1, result = 0;
final int bound = Integer.MIN_VALUE >> 1;
while(true){
dividend -= div;
if(dividend > 0){
if(k == 1) return d == true ? result : -result;
else{
dividend += div;
div = divisor;
k = 1;
continue;
}
}
result += k;
if(div >= bound){ // ****溢出判斷,不能讓 div 左移後溢出了
div <<= 1;
k <<= 1;
}
}
}
}
如果感覺迷糊,看個運算實例就懂了,
假設被除數 dividend = 20,除數 divisor = 3。
加速試減法的流程如下:
- dividend = 20 - 3 = 17,divisor = 3,k = 1,result = 1(result 爲最終要返回的結果)
- dividend = 17 - 3*2 = 11,divisor = 6,k = 2,result = 3(將 k 累加進 result)
- dividend = 11 - 6*2 = -1,dividend < 0,則 divisor = 3,k = 1
- dividend = 11 - 3 = 8,divisor = 3,k = 1,result = 4
- dividend = 8 - 3*2 = 2,divisor = 6,k = 2,result = 6
- dividend = 2 - 6*2 = -10,dividend < 0,則 divisor = 3,k = 1
- dividend = 2 - 3 = -1,dividend < 0,又 k == 1,算法結束,result = 6
所以 20 除 3 的商就爲 6。