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。
這個問題涉及到了計算機如何利用邏輯運算和加減法來求得除法,這個問題之前一度困擾了我很久。
/** * 逼近 * 先定符號 * 結果是正是負還是0? * @param dividend * @param divisor * @return */ public static int divide(int dividend, int divisor) { if (dividend == 0) { return 0; } if (dividend == Integer.MIN_VALUE && divisor == -1) { return Integer.MAX_VALUE; } if (divisor == -1) { return -dividend; } if (divisor == 1) { return dividend; } int res = 0, Sum = 0; boolean plus = false; //同號 if ((dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0)) { plus = true; } //將兩個數都變成負數 dividend = dividend > 0 ? ~dividend + 1 : dividend; divisor = divisor > 0 ? ~divisor + 1 : divisor; for (int i = 30; i > -1; i--) { //被加數未溢位 加之後的結果未溢位 加之後的結果小於被除數 int addNum = divisor<<i; if (addNum>>i == divisor && Sum + addNum < 0 && Sum + addNum >= dividend) { Sum += addNum; res += plus ? 1<<i : -1<<i; } }return res; }
題解(寫程式碼時候的奇怪想法。。。):
首先進行邊界處理之類的。
一開始我採用了二分法猜數字,首先做一個記號記錄結果,然後把被除數(dividend)和除數(divisor)都轉換為正數 (資訊加工),這樣結果就一定是在[0,dividend]。
初始化left = 0, right = dividend,mid = dividend << 1;
然後通過for迴圈累加mid次驗證是否符合結果( mid * divisor <= dividend並無法取到比mid更大的mid'去滿足前面條件);
由於平時並不是經常使用二分碰到了以下問題:
二分的邊界問題:
如何寫出不雜亂的程式碼?
因為常常使用 mid = (left + right)<<1;
故而遺忘了mid還可以向右偏 mid = ((left + right)<<1) + 1;。
因為要保證結果一定在邊界內,故而
left = mid + 1; right = mid - 1;常常不能同時出現(視情況而定吧)。
所以有時候
採用 right = mid - 1; left = mid;這個組合時:
mid = (left + right)<<1; (left + 1 = right) 時候回卡死!
這個時候要mid = ((left + right)<<1) + 1;(向右偏)
當然這樣的思路寫出來的程式碼的結果就是我掛了。。
for迴圈累加代替乘法實在太慢了!!!
然鵝,這時候我想到了一個辦法。
divisor * mid 可以寫成 divisor (m0 * 2^31 + m1 * 2^30 + m2 * 2^31 ....+m30 * 2^0)
然後二的m次方這個東西我是可以通過左移來得到的!
於是我興奮地用這個方法驗證mid對不對。
發現很多邊界問題無法解決
例如:
之前說的右偏碰到Integer.MIN_VALUE
Integer.MIN_VALUE無法轉換為正數
mid取得太大,資料溢位,本來divisor * mid已經超過了Integer.MAX_VALUE。卻還是幾千。。。
於是我處於崩潰的邊緣。。。
這樣搞下去我要屎了!
然鵝,解手的時候。我想:
我可以把所有數都轉換為負數先啊
我可以不用猜測mid是多少啊
我直接從一步一步逼近被除數就行啦??? 好像真的是。。
例如 :
結果如若為101010111...(32位)
那麼我從頭開始的非符號位開始看能不能加進去就好啦! 如若能加進去就逼近了被除數,資料本身溢位,加進去溢位,加進去大於被除數就代表不能加!
其他的都加,反正我要的也是最逼近的數。。。