29. Divide Two Integers

要求不用乘除取余运算计算整形向零取整除法。

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记录下。次幂累加结束,计算一下商,分别记录了41,因此结果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;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值