[LeetCode] Divide Two Integers

本文介绍了一种不使用乘法、除法和取模运算符来实现整数除法的方法。利用二进制表示和移位操作,该算法可以在O(log N)的时间复杂度内完成运算,并详细讨论了正负数情况及整数溢出问题。

Divide two integers without using multiplication, division and mod operator.

显然,如果光用减法太慢。让商为N,那么需要用O(N)的时间。这里要求比较苛刻,连乘法都不能使用,所以只能寄希望于二进制操作了。

这里可以把除数表示为:dividend = 2^i * divisor + 2^(i-1) * divisor + ... + 2^0 * divisor。这样一来,我们所求的商就是各系数之和了,而每个系数都可以通过移位操作获得。

这里可以分两步走:

1) 获得i的值;

2)将各系数求和。

显然每步都是logN的复杂度。

外还有几个细节需要注意:

1)题目没有说明两个输入都为正数,所以还需要考虑负数。不过这里不用考虑除数为0的情况。

2)在处理时可能发生整数溢出,不光是在增大数的绝对值,另外反号也可能导致溢出, 因为int的范围为[-21474836482147483647],正负两边的最大绝对值是不一样的。最简单的方法是用long去处理。

代码如下:

	public int divide(int dividend, int divisor) {
		boolean positive = (dividend > 0 && divisor > 0)
				|| (dividend < 0 && divisor < 0);
		long absDividend = Math.abs((long) dividend);
		long absDivisor = Math.abs((long) divisor);

		int ret = dividePositive(absDividend, absDivisor);
		return positive ? ret : -ret;
	}

	private int dividePositive(long dividend, long divisor) {
		// The first loop computes i.
		int i = 0;
		while (dividend >= divisor << 1) {
			divisor <<= 1;
			i++;
		}

		// The second loop computes the sum of non-zero coefficients.
		int ret = 0;
		while (i >= 0) {
			if (dividend >= divisor) {
				dividend -= divisor;
				ret += 1 << i;
			}
			divisor >>= 1;
			i--;
		}

		return ret;
	}

另外一种实现可以合并两个循环,边求i的时候,边累加各系数,不过这样没有改变时间复杂度。自己测试了下,发现其实合并循环也不会带来什么实质的性能提升,感觉还是两个循环思路更为清晰。

其实上述方法也可以用递归实现,每次求一个系数,然后对余数进行递归处理。递归代码一般更加精简。

	private int dividePositive(long dividend, long divisor) {
		if (dividend < divisor)
			return 0;

		int ret = 1;
		long originalDivisor = divisor;
		while (dividend >= divisor << 1) {
			divisor <<= 1;
			ret <<= 1;
		}

		return ret + dividePositive(dividend - divisor, originalDivisor);
	}

补充:

有的时候面试题里是允许使用乘号的,那么其实还可以使用二分搜索。这样中思路类似于LeetCode里的Sqrt(x)。这里注意的问题是,对于非整除的情况,二分搜索要找到最左边那个乘上divisor后小于dividend的数。这个思路其实类似于在sorted array里找insert position。

	private int dividePositive(long dividend, long divisor) {
		long low = 0, high = dividend;
		long ret = 0;
		while (low <= high) {
			long mid = low + (high - low) / 2;
			long product = mid * divisor;
			if (product == dividend) {
				return (int) mid; // Exact match.
			} else if (product < dividend) {
				ret = mid; // Cache this result.
				low = mid + 1;
			} else { // product > dividend.
				high = mid - 1;
			}
		}

		return (int) ret;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值