LeetCode - Medium - 29. Divide Two Integers
技術標籤:LeetCode演算法與資料結構leetcodemath
Topic
- Math
Description
https://leetcode.com/problems/divide-two-integers/
Given two integers dividend
and divisor
, divide two integers without using multiplication, division, and mod operator.
Return the quotient after dividing dividend
by divisor
.
The integer division should truncate toward zero, which means losing its fractional part. For example, truncate(8.345) = 8
truncate(-2.7335) = -2
.
Note:
- Assume we are dealing with an environment that could only store integers within the 32-bit signed integer range: [−2³¹, 2³¹ − 1]. For this problem, assume that your function returns 2³¹ − 1 when the division result overflows.
Example 1
Input: dividend = 10, divisor = 3
Output: 3
Explanation: 10/3 = truncate(3.33333..) = 3.
Example 2:
Input: dividend = 7, divisor = -3
Output: -2
Explanation: 7/-3 = truncate(-2.33333..) = -2.
Example 3:
Input: dividend = 0, divisor = 1
Output: 0
Example 4:
Input: dividend = 1, divisor = 1
Output: 1
Constraints
- -2³¹ <= dividend, divisor <= 2³¹ - 1
- divisor != 0
Analysis
方法一:我寫的。
一開始,打算將被除數,除數絕對值後(先得出結果正負號),然後二分查詢一個候選商,然後除數累加商值數,累加值與被除數相比,從而得出最佳候選商。但這方法在溢位情況處理很棘手,特別是被除數是Integer.MIN_VALUE
情況(另外,Integer.MIN_VALUE == Math.abs(Integer.MIN_VALUE)
為true
,負數的絕對值還是負數,這情況是不允許的)。因此,放棄這種方法。
換種另一個角度,先得出結果正負號,讓被除數,除數都是變成負數,然後讓除數左移若干位,逼近被除數,逼近得差不多,得出半成商。接著就換成用加法逼近,得出最佳近似商。最後,根據一開始保留結果正負號,得出最佳近似商正負號,並將近似商返回。
方法二 & 方法三:別人寫的,簡潔,優雅。
Submission
public class DivideTwoIntegers {
//方法一:我寫的
public int divide(int dividend, int divisor) {
if (divisor == 0)
throw new ArithmeticException();
if (dividend == Integer.MIN_VALUE && divisor == -1)
return Integer.MAX_VALUE;// 針對溢位情況
boolean negative = dividend > 0 ^ divisor > 0;
if (dividend > 0) dividend = -dividend;
if (divisor > 0) divisor = -divisor;
int quotient = 0, product = 0;
// 粗略得出商
for (int i = 0; i < 32; i++) {
int temp = divisor << i;// 相當於divisor * 2的i次方
// temp >> i != divisor, 溢位情況
if (temp < dividend || temp >> i != divisor) {
break;
} else if (temp > dividend) {
product = temp;
quotient = 1 << i;
} else {
return (negative ? -1 : 1) << i;
}
}
// 較精確逼近商
while (true) {
int temp = product + divisor;
// temp > 0, 溢位情況
if (temp < dividend || temp > 0)
break;
product = temp;
quotient++;
}
return negative ? -quotient : quotient;
}
//方法二:
public int divide2(int A, int B) {
if (A == Integer.MIN_VALUE && B == -1)
return Integer.MAX_VALUE;
int a = Math.abs(A), b = Math.abs(B), res = 0, x = 0;
while (a - b >= 0) {
for (x = 0; a - (b << x << 1) >= 0; x++)
;
res += (1 << x);
a -= (b << x);
}
return (A > 0) == (B > 0) ? res : -res;
}
//方法三:
public int divide3(int A, int B) {
if (A == Integer.MIN_VALUE && B == -1)
return Integer.MAX_VALUE;
int a = Math.abs(A), b = Math.abs(B), res = 0;
for (int x = 31; x >= 0; x--)
if ((a >>> x) - b >= 0) {
res += 1 << x;
a -= b << x;
}
return (A > 0) == (B > 0) ? res : -res;
}
}
Test
import static org.junit.Assert.*;
import org.junit.Test;
public class DivideTwoIntegersTest {
@Test
public void test() {
DivideTwoIntegers obj = new DivideTwoIntegers();
assertEquals(3, obj.divide(10, 3));
assertEquals(-2, obj.divide(7, -3));
assertEquals(0, obj.divide(0, 1));
assertEquals(1, obj.divide(1, 1));
assertEquals(Integer.MAX_VALUE, obj.divide(Integer.MAX_VALUE, 1));
assertEquals(Integer.MIN_VALUE, obj.divide(Integer.MIN_VALUE, 1));
assertEquals(0, obj.divide(1, Integer.MIN_VALUE));
assertEquals(-1, obj.divide(-1, 1));
assertEquals(1, obj.divide(Integer.MAX_VALUE, Integer.MAX_VALUE));
assertEquals(Integer.MAX_VALUE, obj.divide(Integer.MAX_VALUE, 1));
assertEquals(-Integer.MAX_VALUE, obj.divide(Integer.MAX_VALUE, -1));
assertEquals(-Integer.MAX_VALUE, obj.divide(-Integer.MAX_VALUE, 1));
assertEquals(Integer.MAX_VALUE, obj.divide(-Integer.MAX_VALUE, -1));
assertEquals(0, obj.divide(1, Integer.MAX_VALUE));
assertEquals(0, obj.divide(1, -Integer.MAX_VALUE));
for (int i = -100; i <= 100; i++) {
for (int j = 1; j < 100; j++) {
assertEquals(i / j, obj.divide(i, j));
}
}
}
@Test
public void test2() {
DivideTwoIntegers obj = new DivideTwoIntegers();
assertEquals(3, obj.divide2(10, 3));
assertEquals(-2, obj.divide2(7, -3));
assertEquals(0, obj.divide2(0, 1));
assertEquals(1, obj.divide2(1, 1));
assertEquals(Integer.MAX_VALUE, obj.divide2(Integer.MAX_VALUE, 1));
assertEquals(Integer.MIN_VALUE, obj.divide2(Integer.MIN_VALUE, 1));
assertEquals(0, obj.divide2(1, Integer.MIN_VALUE));
assertEquals(-1, obj.divide2(-1, 1));
assertEquals(1, obj.divide2(Integer.MAX_VALUE, Integer.MAX_VALUE));
assertEquals(Integer.MAX_VALUE, obj.divide2(Integer.MAX_VALUE, 1));
assertEquals(-Integer.MAX_VALUE, obj.divide2(Integer.MAX_VALUE, -1));
assertEquals(-Integer.MAX_VALUE, obj.divide2(-Integer.MAX_VALUE, 1));
assertEquals(Integer.MAX_VALUE, obj.divide2(-Integer.MAX_VALUE, -1));
assertEquals(0, obj.divide2(1, Integer.MAX_VALUE));
assertEquals(0, obj.divide2(1, -Integer.MAX_VALUE));
for (int i = -100; i <= 100; i++) {
for (int j = 1; j < 100; j++) {
assertEquals(i / j, obj.divide2(i, j));
}
}
}
@Test
public void test3() {
DivideTwoIntegers obj = new DivideTwoIntegers();
assertEquals(3, obj.divide3(10, 3));
assertEquals(-2, obj.divide3(7, -3));
assertEquals(0, obj.divide3(0, 1));
assertEquals(1, obj.divide3(1, 1));
assertEquals(Integer.MAX_VALUE, obj.divide3(Integer.MAX_VALUE, 1));
assertEquals(Integer.MIN_VALUE, obj.divide3(Integer.MIN_VALUE, 1));
assertEquals(0, obj.divide3(1, Integer.MIN_VALUE));
assertEquals(-1, obj.divide3(-1, 1));
assertEquals(1, obj.divide3(Integer.MAX_VALUE, Integer.MAX_VALUE));
assertEquals(Integer.MAX_VALUE, obj.divide3(Integer.MAX_VALUE, 1));
assertEquals(-Integer.MAX_VALUE, obj.divide3(Integer.MAX_VALUE, -1));
assertEquals(-Integer.MAX_VALUE, obj.divide3(-Integer.MAX_VALUE, 1));
assertEquals(Integer.MAX_VALUE, obj.divide3(-Integer.MAX_VALUE, -1));
assertEquals(0, obj.divide3(1, Integer.MAX_VALUE));
assertEquals(0, obj.divide3(1, -Integer.MAX_VALUE));
for (int i = -100; i <= 100; i++) {
for (int j = 1; j < 100; j++) {
assertEquals(i / j, obj.divide3(i, j));
}
}
}
}