leetcode 29. 兩數相除(Java版)

題目名,兩數相除,表面看起來人畜無害。結果…,他喵的提交十幾次才過。

題目描述(題目難度,中等)

給定兩個整數,被除數 dividend 和除數 divisor。將兩數相除,要求不使用乘法、除法和 mod 運算符。

返回被除數 dividend 除以除數 divisor 得到的商。

示例 1:

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

示例 2:

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

說明:

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

題目求解

解法一

最開始看題目也沒什麼時間複雜度的要求,以爲題目難點主要在於不能使用乘除和取餘運算。於是想到一個直白的試減法就提交了,試減法的思路就是每次循環都將被除數減去一個除數,這樣最終被除數被減去的次數就是商。
其實這個解法在英文版官網上是可以提交通過的,這樣也比較符合劃定的中等難度的級別。但到了中文版,就會說超出時間限制,無法通過。所以沒辦法,只能優化了這個解法,最終在中文版網站提交通過。
這個試減法,我是將輸入的被除數和除數統一轉換爲負數,再進行試減的。爲什麼這樣呢,我們平常不是習慣於正數的計算嗎。其實我最開始也是統一轉化爲正數來做的,但這樣做有一個問題就是,如果被除數爲 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。
加速試減法的流程如下:

  1. dividend = 20 - 3 = 17,divisor = 3,k = 1,result = 1(result 爲最終要返回的結果)
  2. dividend = 17 - 3*2 = 11,divisor = 6,k = 2,result = 3(將 k 累加進 result)
  3. dividend = 11 - 6*2 = -1,dividend < 0,則 divisor = 3,k = 1
  4. dividend = 11 - 3 = 8,divisor = 3,k = 1,result = 4
  5. dividend = 8 - 3*2 = 2,divisor = 6,k = 2,result = 6
  6. dividend = 2 - 6*2 = -10,dividend < 0,則 divisor = 3,k = 1
  7. dividend = 2 - 3 = -1,dividend < 0,又 k == 1,算法結束,result = 6

所以 20 除 3 的商就爲 6。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章