Divide Two Integers

Divide Two Integers

题目

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.

Example 1:

Input: dividend = 10, divisor = 3
Output: 3

Example 2:

Input: dividend = 7, divisor = -3
Output: -2

Note:

  • Both dividend and divisor will be 32-bit signed integers.
  • The divisor will never be 0.
  • Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31}-1] [231,2311]. For the purpose of this problem, assume that your function returns 2 31 − 1 2^{31} - 1 2311 when the division result overflows.

分析

题目给出一个被除数和一个除数,这两个数都是int类型(有符号整数)。
要求我们不用乘法、除法和模运算,求出这两者的商。
并且给出的除数是不会是0,如果溢出了,则让结果为 2 32 − 1 2^{32} - 1 2321

最笨的解法就是不断将被除数的绝对值减去除数的绝对值,算出有多少个除数。但是当两个数的绝对值相差越大,时间复杂度越高。当除数是 ± 1 \pm{1} ±1时,循环次数为被除数的绝对值。

另一种解法是模拟我们手算除法的过程来计算出其商。可以将两个数都转化为String类型,然后用char[]数组存起来,之后进行模拟计算。该方法需要将int转化为String类型,而且还需要自己实现String之间的加法和减法,比较麻烦,效率也不是很高。

第三种解法也是模拟手算除法的过程,只不过是计算二进制的除法而不是通常的十进制除法。相较于上一种解法,更加简洁,也比较简单。
由于其取值范围是 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31}-1] [231,2311],因此当被除数是 − 2 31 -2^{31} 231,除数是-1时,商是 2 31 2^{31} 231,会产生溢出;其他情况不会溢出(不考虑除数为0的情况)。

  • 首先,需要将负数转化为整数,就是取反加一a = (~a) + 1),但是当某一个数是 − 2 31 -2^{31} 231,即0x80000000时,取反加1之后仍然时0x80000000,我们这时候认为他是整数就可以了,在后面计算时需要稍加注意即可。
  • 然后当转化后的被除数 - 转化后的除数 < 0时,此时被除数小于除数,直接返回0。
    需要注意的是,比较时不能使用转化后的被除数 < 转化后的除数,因为当其中一个数是0x80000000时,直接比较时会将其当作负数导致出错。例如a = 0x80000000; b = 1,此时a - b = 0x7fffffff > 0,和我们要的结果相符合。但是如果使用a < b进行比较,由于a是负数,这样得到的结果和我们想要的结果不符合。
  • 之后算出两个数的位数(例如:0x00001000总共有16位)。假设分别为pos1pos2,假设临时商为result。取出被除数的前pos2bits,记为tempDividend
    tempDividend - 除数 >= 0(注意:这里也不能使用tempDividend > 除数),则result = (result < 1) + 1,将tempDividend减去除数得到的结果右移一位,加上被除数下一位的值作为新的tempDividend。然后进行下一轮直到被除数没有未使用的bit
    tempDividend - 除数 < 0,则result = result < 1。将tempDividend右移一位,加上被除数下一位的值作为新的tempDividend。然后进行下一轮直到被除数没有未使用的bit
  • 最后根据结果的正负调整result即可。

举个例子:
例如对于10除以3,转化为二进制数就是1010除以11,位数分别是42
第一次计算时10 - 11 >= 0falseresult = reuslt << 1 = 0,更新后的tempDividend101
第二次计算时101 - 11 >= 0trueresult = (result << 1) + 1 = 1,更新后的tempDividend((101 - 11) << 1) + 0 = 100
第三次计算时100 - 11 >= 0trueresult = (result << 1) + 1 = 11,此时被除数没有未使用的bit,循环到此结束。
由于两个数都是正数,因此结果为正数11,转化为十进制就是3,于实际结果相符合。

代码如下

class Solution {
    public int divide(int dividend, int divisor) {
    	boolean positive = true;  // 结果是不是正数
    	int result = 0;
    	int min = Integer.MIN_VALUE;  // 0x80000000
    	int max = Integer.MAX_VALUE;  // 0x7fffffff
    	if (dividend == min && divisor == -1) {  // 只有这一种情况会溢出
    		return max;
    	}
    	if (dividend >>> 31 == 1) {  // 无符号右移,判断是不是负数
    		positive = !positive;
    		dividend = (~dividend) + 1;
    	}
    	if (divisor >>> 31 == 1) {  // 无符号右移,判断是不是负数
    		positive = !positive;
    		divisor = (~divisor) + 1;
    	}
    	if (dividend - divisor < 0) {  // 被除数小于除数
    		return result;
    	}
    	int pos1 = findFirstOne(dividend);
    	int pos2 = findFirstOne(divisor);
    	int tempDividend = dividend >>> (pos1 - pos2);
    	int res;
    	if (tempDividend - divisor >= 0) {
    		result = 1;
    		res = tempDividend - divisor;
    	} else {
			result = 0;
			res = tempDividend;
		}
    	for (int i = pos1 - pos2 - 1; i >= 0; i--) {
    		tempDividend = (res << 1) + ((dividend & (1 << i)) >> i);
    		if (tempDividend - divisor >= 0) {
    			result = (result << 1) + 1;
    			res = tempDividend - divisor;
    		} else {
				result = result << 1;
				res = tempDividend;
			}
    	}
    	if (positive) {
    		return result;
    	} else {
			return (~result) + 1;
		}
    }
    /**
     * @param num
     * @return 第一个非零bit的位置,最高位为31,最低位为0
     */
    private int findFirstOne(int num) {
    	int pos = 0;
    	while (num > 0) {
    		num = num << 1;
    		pos++;
    	}
    	return 31 - pos;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值