LeetCode第[29]题(Java):Divide Two Integers
题目:两整数相除
难度:Medium
题目内容:
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.
翻译:
给定两个整数,被除数和除数,不使用乘法,除法和mod运算符。
在除以除数后,返回商数。
整数除法应该截断为零。
Example 1:
Input: dividend = 10, divisor = 3 Output: 3
Example 2:
Input: dividend = 7, divisor = -3 Output: -2
我的思路:因为此题需要考虑的边界情况太多,而重点考察的却不是这些,故只做算法分析,不求跑通所有代码。
最笨的方法:直接循环减去除数。。。
方法二:之前印象中好像在哪见过,每次循环都减去能减的最大的除数*2^n,并且每次循环把2次幂都加起来。商就是这些2次幂的和。
举个例子:20/3,
第一次循环,20>3*1,20>3*2,20>3*2*2,已经最大,打住, dividend=20-3*2*2=8, ans = 2*2 = 4
第二次循环,8>3*1,8>3*2,已经最大,打住, dividend=8-3*2=2, ans = 4 + 2 = 6
dividend < 3 退出循环,最后 ans = 6
MyCode:
1 public int divide(int dividend, int divisor) { 2 if (dividend != 0 && dividend == -dividend && divisor == -1) { 3 return -(dividend+1); 4 } 5 return div(dividend, divisor); 6 } 7 public static int div(int x, int y) { 8 int tag = 1; 9 if ((x ^ y) < 0) { // 位运算符优先级很低,记得加括号 10 tag = -1; 11 if (x < 0) { 12 x = -x; 13 } else { 14 y = -y; 15 } 16 } else if (x < 0) { 17 x = -x; 18 y = -y; 19 } // 都设置成正整数 20 int ans = 0; 21 while (x >= y) { 22 int flag = 1; 23 while ((x >> 1) >= y * flag) { // 如果使用x >= y * (flag << 1) 则有可能溢出 24 flag <<= 1; 25 } 26 x -= y*flag; 27 ans += flag; 28 } 29 return ans*tag; 30 }
编码过程中出现问题:
1、bad operand types for binary operator “^”
————位运算符的优先级很低,甚至比==都要低,所以遇见位运算符要记得加括号。
2、溢出
————在23行处,进行了>>1(乘以2)的判断运算,此时可能溢出,所以在进行判断运算时,即先运算再判断是否能如此运算的时候,应该将判断符(==、>=等)两边的运算符平衡一下,如果一边可能溢出,则将此运算符移至另一边。【此处与之前的leetCode的某一题貌似是 数字反转 很像】
答案代码:
1 public int divide(int dividend, int divisor) { 2 if(dividend<<1 == 0 && divisor == -1){// x /y = -2^32 / -1, overflow 3 return (-1) >>> 1; 4 } 5 6 if(divisor == 1 || dividend == 0){ // x / y = x / 1 or 0 / y 7 return dividend; 8 } 9 if(divisor == dividend) return 1; // x / y when x == y 10 else if(divisor<<1 == 0) return 0; // x / y = x / (-2^32) 11 12 int sign; //sign 13 if(divisor < 0 && dividend < 0 || divisor > 0 && dividend > 0){ 14 sign = 1; 15 }else{ 16 sign = -1; 17 } 18 19 divisor = divisor < 0 ? -divisor : divisor; // positive divisor 20 int left = dividend, res = 0; 21 if(dividend << 1 == 0){ // if x / y = (-2^32) / y, first we add x by y, then -x will not overflow 22 left += divisor; 23 res++; 24 } 25 left = left > 0 ? left : -left; 26 27 int tDivisor = divisor, tA = 1; 28 while(left >= divisor){ 29 tDivisor = divisor; 30 tA = 1; 31 while(tDivisor << 1 > 0 && left >= tDivisor << 1){ // max tDivisor = tA * divisor <= left 32 tDivisor <<= 1; 33 tA <<= 1; 34 } 35 res += tA; 36 left -= tDivisor; 37 } 38 return sign == 1 ? res : -res; 39 }
其实真正的代码从27行开始,和我代码是一个意思,不过前面多出了很多处理边界的情况,在此不做讨论。
另外一种方法——二分法:
和朋友讨论发现另外一种巧妙地解法,用二分法:
1 public static int div2(int x, int y) { 2 int tag = 1; 3 if ((x ^ y) < 0) { // 位运算符优先级很低,记得加括号 4 tag = -1; 5 if (x < 0) { 6 x = -x; 7 } else { 8 y = -y; 9 } 10 } else if (x < 0) { 11 x = -x; 12 y = -y; 13 } 14 int ans = 0; // 算法从此处开始 15 int low = 1; 16 int high = x; 17 while (low <= high) { 18 int mid = low + ((high-low)>>1); 19 int rest = x - y*mid; 20 System.out.println(rest); 21 if (rest >= 0 && rest < y) { 22 ans = mid; 23 break; 24 } 25 if (rest < 0) { 26 high = mid - 1; 27 } else if (rest >= y) { 28 low = mid + 1; 29 } 30 } 31 return ans * tag; 32 }