【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除

剑指 Offer II 001. 整数除法

【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除_第1张图片
【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除_第2张图片

解题思路

  • 在计算的时候将负数转化为正数,对于32位整数而言,最小的正数是-2^31, 将其转化为正数是2^31,导致溢出。因此将正数转化为负数不会导致溢出。
  • 设置一个变量,用来记录正数个数,以便在最后的结果调整正负号。
  • 特殊情况,可能溢出的情况讨论,由于是整数除法,除数不为0,商的值一定小于等于被除数的绝对值,因此,int型溢出只有一种情况,(-2^31)/(-1) = 2^31。

最容易想到的是通过减法代替除法,但是会超时

class Solution {
    public int divide(int a, int b) {
        if(a == 0)return 0;
		int symbolNum = (a>0)^(b>0)? -1:1 ;//可以对下面的代码段进行优化
		/*
        int symbolNum = 1;
        if(a>0&&b>0 || a<0&&b<0) symbolNum = 1;
        else{
            symbolNum = -1;
        }*/

        if(a==Integer.MIN_VALUE && b==-1) return Integer.MAX_VALUE;

        if(a>0)a = -a;
        if(b>0)b = -b;
        int ans = 0;
        while(a <= b){
            a -= b;
            ans++;
        }
        return symbolNum == -1? -ans:ans;
    }
}

优化思路

不使用减法那种被除数对除数一次一次减的形式(在被除数过大而除数很小的情况下会超时),而是试图减2个、4个、8个....找到最大的2的n次方。将被除数减去除数的2的n次方倍,然后将剩余的被除数重复前面的步骤。
例如:
为了求得15/2的商,先从15里减去8 (23),得到7;再从7里减去4(22),得到3;再从3里减去2(2^1),得到1,此时1小于2,因此商是3+2+1=7。

在计算两个负数的除法时,防止每次计算除数的2倍,会导致大数溢出,要保证计算2倍之后的数大于(-231)/2。如果时计算两个正数的除法,要保证计算2倍之后的数小于(231-1)/2。

class Solution {
    public int divide(int a, int b) {
        //int 型整数的除法只有一种情况会导致溢出,即(-2^31)/(-1)
        if(a==Integer.MIN_VALUE && b==-1) return Integer.MAX_VALUE;
        if(a == 0 || b == 1)return a;
        else if(b == -1) return -a;
        int symbolNum = (a>0)^(b>0)? -1:1 ;
        // 由于(-2^31) 转换为正数会溢出,但是任意正数转换为负数都不会溢出
        // 故,记录负数的个数,并将正数转换为负数方便统一计算
        if(a>0)a = -a;
        if(b>0)b = -b;
        
        // while(a <= b){//这样写确实可以过案例,但是会超时
        //     a -= b;
        //     ans++;
        // }
        if(a == b)return symbolNum == -1? -1:1;
        int ans = 0;
        while(a<=b){//小于是因为现在a,b都是负数
            int value = b;//统计最多有几个减数(2的n次方个)
            int tmpQuotient = 1;
            while(value + value > Integer.MIN_VALUE && value + value >= a){//减到不够了为止
                value += value;
                tmpQuotient += tmpQuotient;
            }
            ans +=tmpQuotient;
            a -= value;
        }


        return symbolNum == -1? -ans:ans;
    }
    

}

但上述代码只通过了700+样例,找了半天发现是value + value > Integer.MIN_VALUE这里出问题了,要改成value >0xc0000000。(0xc0000000是Integer.MIN_VALUE的一半)

完整代码贴在这儿

class Solution {
    public int divide(int a, int b) {
        //int 型整数的除法只有一种情况会导致溢出,即(-2^31)/(-1)
        if(a==Integer.MIN_VALUE && b==-1) return Integer.MAX_VALUE;
        if(a == 0 || b == 1)return a;
        else if(b == -1) return -a;
        int symbolNum = (a>0)^(b>0)? -1:1 ;
        // 由于(-2^31) 转换为正数会溢出,但是任意正数转换为负数都不会溢出
        // 故,记录负数的个数,并将正数转换为负数方便统一计算
        if(a>0)a = -a;
        if(b>0)b = -b;
        
        // while(a <= b){//这样写确实可以过案例,但是会超时
        //     a -= b;
        //     ans++;
        // }
        if(a == b)return symbolNum == -1? -1:1;
        int ans = 0;
        while(a<=b){//小于是因为现在a,b都是负数
            int value = b;//统计最多有几个减数(2的n次方个)
            int tmpQuotient = 1;
            while(value >0xc0000000 && value + value >= a){//减到不够了为止
                value += value;
                tmpQuotient += tmpQuotient;
            }
            ans +=tmpQuotient;
            a -= value;
        }


        return symbolNum == -1? -ans:ans;
    }
    

}

这里补充一下int 类型数据的最大值,最小值及其十六进制表示方式:
在int类型(32位)中:

正整数的最大值为 0x7fffffff 也就是十进制的 2147483647

正整数的最小值为 0x00000001 也就是十进制的 1

0表示为:0x00000000

负整数的最大值为 0xffffffff 也就是十进制的 -1

负整数的最小值为 0x80000000 也就是十进制的 -2147483648

你可能感兴趣的:(【剑指 Offer II 001. 整数除法】同leedcode 29.两数相除)