要求不用乘除取余运算计算整形向零取整除法。
Example 1:
Input: dividend = 10, divisor = 3
Output: 3
Example 2:
Input: dividend = 7, divisor = -3
Output: -2
1. 不能超时,所以要用二分法来加速
2. 边界条件。注意不要超过32位int的范围。特别恶心的:因为-2147483648~2147483647,看出正数比负数的少一个。有的方法为了防止溢出用long来处理,但是其实可以事先进行判断,如果除数是-Integer.MIN_VALUE,除数是-1,就返回Integer.MAX_VALUE。因为负值的范围更大,所以将两个数字都转成负数,预先用异或进行判断结果的正负。
class Solution {
public int divide(int dividend, int divisor) {
if(divisor==0 || (dividend==Integer.MIN_VALUE && divisor==-1))
return Integer.MAX_VALUE;
if(dividend==0)
return 0;
boolean neg = (dividend<0) ^ (divisor<0);
int ndividend = (dividend<0)? dividend : (-dividend);
int ndivisor = (divisor<0)? divisor : (-divisor);
return neg? -dividHelper(ndividend,ndivisor) : dividHelper(ndividend, ndivisor);
}
private int dividHelper(int nded, int ndsor){
if(nded>ndsor)
return 0;
int res=1, last=ndsor, cur=ndsor<<1;
while(cur>nded && cur<last){//注意这里cur<last是为了不陷入死循环,因为最后累加溢出时cur会变成正数
res <<= 1;
last = cur;
cur <<= 1;
}
return res+dividHelper(nded-last, ndsor);
}
}
注意循环判断条件中必须判断是不是溢出,否则当除数比被除数小太多时就会导致cur溢出:(以2147483647 / 3为例)
res=134217728
last=-402653184
cur=-805306368
res=268435456
last=-805306368
cur=-1610612736
res=536870912
last=-1610612736
cur=1073741824 //这里溢出了
res=1073741824
last=1073741824
cur=-2147483648
另一种方法,二项式分解:
参考 https://kingsfish.github.io/2017/10/11/Leetcode-29-Divide-Two-Integers/
举个例子,假设除数为3,被除数为16,那么商应该是5。我们从2的0次幂与除数的乘积也即20x3=3开始,幂次逐渐增加,直至超过被除数。可以看出,当幂次达到2时刚好超过16(3x20+3x21+3x22=21>16)。那么从3x22开始往下累加,3x22=12>16,那么记录下22=4。再看3x21,发现3x22+3x21=18>16,因此略过21=2。再看3x20,发现发现3x22+3x20=15<16,那么将20=1记录下。次幂累加结束,计算一下商,分别记录了4
和1
,因此结果4+1=5,此答案也即为最终的商。
算法大概的思路差不多讲完了,还需要注意的就是边界问题,只有一个边界特例需要考虑Integer.MIN_VALUE和-1
。此时的结果超过了Integer
所能表示的最大范围,因此需要特殊处理。其次,为了简单起见,我们将除数和被除数的符号进行记录,然后将其转换为正数进行计算,这也涉及到溢出的问题,Integer.MIN_VALUE
转换为正数之后会超过32位Integer
所能表示的范围,因此在代码中使用long
类型进行计算防止溢出。
此算法时间复杂度是O(logn)
。空间复杂度为O(logn)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public int divide(int dividend, int divisor) { if(dividend == -2147483648 && divisor == -1){ //防止溢出 return 2147483647; } ArrayList<Long> list = new ArrayList<>(); int end = dividend > 0 ? 1 : 0; int sor = divisor > 0 ? 1 : 0; long a = Math.abs((long)dividend); long b = Math.abs((long)divisor); int ret = 0; list.add(b); long i = 1; //记录除数与二次幂的乘积 while (b <= a){ b += b; i += i; list.add(b); } long sum = 0; //累加 for (int j = list.size() - 2; j >= 0; j --){ i >>= 1; if (sum + list.get(j) <= a){ ret += i; sum += list.get(j); } } //根据除数以及被除数的正负确定返回值正负 if (end + sor == 1){ return -ret; } else { return ret; } } |