leetcode 29. 两数相除(Java版)

题目名,两数相除,表面看起来人畜无害。结果…,他喵的提交十几次才过。

题目描述(题目难度,中等)

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

返回被除数 dividend 除以除数 divisor 得到的商。

示例 1:

输入: dividend = 10, divisor = 3
输出: 3

示例 2:

输入: dividend = 7, divisor = -3
输出: -2

说明:

  • 被除数和除数均为 32 位有符号整数。
  • 除数不为 0。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [231−2^{31}, 23112^{31} − 1]。本题中,如果除法结果溢出,则返回 23112^{31} − 1

题目求解

解法一

最开始看题目也没什么时间复杂度的要求,以为题目难点主要在于不能使用乘除和取余运算。于是想到一个直白的试减法就提交了,试减法的思路就是每次循环都将被除数减去一个除数,这样最终被除数被减去的次数就是商。
其实这个解法在英文版官网上是可以提交通过的,这样也比较符合划定的中等难度的级别。但到了中文版,就会说超出时间限制,无法通过。所以没办法,只能优化了这个解法,最终在中文版网站提交通过。
这个试减法,我是将输入的被除数和除数统一转换为负数,再进行试减的。为什么这样呢,我们平常不是习惯于正数的计算吗。其实我最开始也是统一转化为正数来做的,但这样做有一个问题就是,如果被除数为 Integer.MIN_VALUE,则它无法转化为对应的正数。需要注意:Integer.MIN_VALUE == -Integer.MIN_VALUE

class Solution {
    public int divide(int dividend, int divisor) {
    	if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 题意
    	if(divisor == Integer.MIN_VALUE){ // **此处如不特判,后面会陷入死循环
    		if(dividend == Integer.MIN_VALUE) return 1;
    		else return 0;
    	}
    	if(divisor == -1) return -dividend;
    	if(divisor == 1) return dividend;
        boolean d = true; // 同号标志
        if(dividend <= 0 && divisor > 0){
        	d = false;
        	divisor = -divisor;
        }else if(dividend >= 0 && divisor < 0){
        	d = false;
        	dividend = -dividend;
        }else if(dividend >= 0 && divisor > 0){
        	dividend = -dividend;
        	divisor = -divisor;
        }
        int result = 0;
        while(true){
        	dividend -= divisor;
        	if(dividend > 0) return d == true ? result : -result;
        	++result;
        }
    }
}

解法二

加速的试减法,使用移位运算来加速试减。

class Solution {
    public int divide(int dividend, int divisor) {
    	if(dividend == Integer.MIN_VALUE && divisor == -1) return Integer.MAX_VALUE; // 题意
    	if(divisor == Integer.MIN_VALUE){ // **此处如不特判,后面会陷入死循环
    		if(dividend == Integer.MIN_VALUE) return 1;
    		else return 0;
    	}
    	if(divisor == -1) return -dividend;
    	if(divisor == 1) return dividend;
        boolean d = true; // 同号标志
        if(dividend <= 0 && divisor > 0){
        	d = false;
        	divisor = -divisor;
        }else if(dividend >= 0 && divisor < 0){
        	d = false;
        	dividend = -dividend;
        }else if(dividend >= 0 && divisor > 0){
        	dividend = -dividend;
        	divisor = -divisor;
        }
        int div = divisor, k = 1, result = 0;
        final int bound = Integer.MIN_VALUE >> 1;
        while(true){
        	dividend -= div;
        	if(dividend > 0){
        		if(k == 1) return d == true ? result : -result;
        		else{
        			dividend += div;
        			div = divisor;
        			k = 1;
        			continue;
        		}
        	}
        	result += k;
        	if(div >= bound){ // ****溢出判断,不能让 div 左移后溢出了        		
        		div <<= 1;
        		k <<= 1;
        	}
        }
    }
}

如果感觉迷糊,看个运算实例就懂了,
假设被除数 dividend = 20,除数 divisor = 3。
加速试减法的流程如下:

  1. dividend = 20 - 3 = 17,divisor = 3,k = 1,result = 1(result 为最终要返回的结果)
  2. dividend = 17 - 3*2 = 11,divisor = 6,k = 2,result = 3(将 k 累加进 result)
  3. dividend = 11 - 6*2 = -1,dividend < 0,则 divisor = 3,k = 1
  4. dividend = 11 - 3 = 8,divisor = 3,k = 1,result = 4
  5. dividend = 8 - 3*2 = 2,divisor = 6,k = 2,result = 6
  6. dividend = 2 - 6*2 = -10,dividend < 0,则 divisor = 3,k = 1
  7. dividend = 2 - 3 = -1,dividend < 0,又 k == 1,算法结束,result = 6

所以 20 除 3 的商就为 6。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章