https://leetcode.com/problems/divide-two-integers/
Divide two integers without using multiplication, division and mod operator.
If it is overflow, return MAX_INT.
解题思路:
很讨厌做这种类型的题目,一来不熟悉,二来要注意的地方很多,比如溢出问题,计数的边界,只能硬着头皮上。题目要求不能使用乘法、除法和mod运算,来实现除法。那就只有用加法和减法了,(后来发现还有位运算,其实就是除以2和乘以2)。
先写了个最简单的,只考虑了正负问题,然后用被除数一直减去除数,只到被除数小于除数,减了多少次就是商了。当然不可能对,一定会超时。
public class Solution { public int divide(int dividend, int divisor) { int isNegative = 1; if(dividend < 0){ dividend = -dividend; isNegative = -isNegative; } if(divisor < 0){ divisor = -divisor; isNegative = -isNegative; } if(divisor == 0){ return Integer.MAX_VALUE; } int result = 0; while(dividend >= divisor){ dividend = dividend - divisor; result++; } return isNegative < 0 ? -result : result; } }
然后又写了个改进版的。因为任何数字都可以表示为以10为底的N次多项式的和,即x= a1*10^n + a2*10^n-1+...+an*10^1+an+1*10^0。所以可以把被除数一直乘以10,到达被除数的位置,再开始用上面的方法计算。比如计算500/2,不必要像上面那样一直500-2计算次数。而是首先算500-200的次数,然后用剩下的100算100-20的次数,结果就是25了。
public class Solution { public int divide(int dividend, int divisor) { int result = 0; if(divisor == 0){ return Integer.MAX_VALUE; } if(divisor == Integer.MIN_VALUE){ if(dividend == Integer.MIN_VALUE){ return 1; //-2147483648 / -2147483648 = 1 }else{ return 0; // x / -2147483648 = 0; } } //-2147483648 / -1 = 2147483648溢出 if(dividend == Integer.MIN_VALUE && divisor == -1){ return Integer.MAX_VALUE; } //-2147483648 / x变化成2147483648 / x会溢出,所以先处理掉一次divisor,结果最后加上这个1 int last = 0; if(dividend == Integer.MIN_VALUE){ dividend += Math.abs(divisor); last = 1; } int isNegative = 1; if(dividend < 0){ dividend = -dividend; isNegative = -isNegative; } if(divisor < 0){ divisor = -divisor; isNegative = -isNegative; } int digit = 0; //先从最高位算起,除数一直乘以10会溢出,比如2147483647 / 1,只能乘到倒数第二次 while(dividend / 10 >= divisor){ divisor *= 10; digit++; } while(digit >= 0){ int temp = 0; //当前位的结果 while(dividend >= divisor){ dividend = dividend - divisor; temp++; } divisor /= 10; //算下一位的结果,除数要除以10 result = result * 10 + temp; digit--; } result += last; return isNegative < 0 ? -result : result; } }
上面的方法,最关键的是各种溢出值得处理,因为Integer.MIN_VALUE=-2147483648,而Integer.MAX_VALUE=2147483647,绝对值是要小1的。所以处理除数或者被除数为MIN_VALUE的时候,会遇到很多问题。比如,-2147483648作为除数去除任何数,都为0,除了-2147483648/-2147483648=1.
而-2147483648除以任何数字,将他变为2147483648的时候都会溢出,所以只能用它先减去一次除数的绝对值,再处理。等所有的结果都算出来,最后再加上这个1。
当然上面的方法是不允许的,虽然结果AC,因为题目不能用乘法。
后来看了网友的解法,用位运算即可解决了。因为一个数字可以写成10进制的,也能写成2进制的,就是以2为底的多项式的和。所以向左移位(<<)其实就是乘以2,向右移位(>>)其实就是除以2。这样等于用位运算实现了乘除法。上面的解法里,只要把所有的除以10改为>>1,乘以10改为<<1,就可以了。
最后要注意的是,位运算的优先级比加减法还低,所以一定要加括号。
public class Solution { public int divide(int dividend, int divisor) { int result = 0; if(divisor == 0){ return Integer.MAX_VALUE; } if(divisor == Integer.MIN_VALUE){ if(dividend == Integer.MIN_VALUE){ return 1; //-2147483648 / -2147483648 = 1 }else{ return 0; // x / -2147483648 = 0; } } //-2147483648 / -1 = 2147483648溢出 if(dividend == Integer.MIN_VALUE && divisor == -1){ return Integer.MAX_VALUE; } //-2147483648 / x变化成2147483648 / x会溢出,所以先处理掉一次divisor,结果最后加上这个1 int last = 0; if(dividend == Integer.MIN_VALUE){ dividend += Math.abs(divisor); last = 1; } int isNegative = 1; if(dividend < 0){ dividend = -dividend; isNegative = -isNegative; } if(divisor < 0){ divisor = -divisor; isNegative = -isNegative; } int digit = 0; //先从最高位算起,除数一直乘以10会溢出,比如2147483647 / 1,只能乘到倒数第二次 while(dividend >>1 >= divisor){ divisor <<= 1; digit++; } while(digit >= 0){ int temp = 0; //当前位的结果 while(dividend >= divisor){ dividend = dividend - divisor; temp++; } divisor >>= 1; //算下一位的结果,除数要除以10 result = (result << 1) + temp; //注意位运算符的优先级很低,不如加法 digit--; } result += last; return isNegative < 0 ? -result : result; } }
这道题我是没做出来,而且意外处理需要考虑的比较多。但是面试的时候不失为一道比较好的题目,可以用来结合设计test case来考,还是需要好好掌握的。