LeetCode29-两数相除

LeetCode29-两数相除

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

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

示例 1:

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

示例 2:

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

说明:

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

一、思路

(一)暴力法

这里要求不使用乘法、除法、取模运算,并且计算结果是向下取整的,可以考虑使用累加法(循环减也可以):
通过循环,将减数累加起来,直到减数大于或者等于被减数为止,这个方法最大的缺点就是减数很小的时候,十分耗时

出错1:被除数有取值范围

当被除数=INT32_MIN时,取绝对值会超过表示范围。
解决办法:使用long long保存int表示不了的大数

出错2:除数也有取值范围

解决办法,判断一下,如果除数的绝对值大于被除数,直接返回0

C++代码:

class Solution {
public:
	int divide(int dividend, int divisor) {
		if (dividend == INT32_MIN && divisor == 1)
			return INT32_MIN;

		if (dividend == INT32_MIN && divisor == -1)
			return INT32_MAX;

		long long sum = 0, count = 0;
		long long dividend_l = dividend, divisor_l = divisor;

		if (abs(divisor_l) > abs(dividend_l))
			return 0;

		int flag1 = 0, flag2 = 0;

		if (dividend < 0)
			flag1 = 1;
		if (divisor < 0)
			flag2 = 1;

		while (sum < abs(dividend_l)) {
			sum += abs(divisor_l);
			count++;
		}
		if (sum > abs(dividend_l))
			count--;

		if (flag1 + flag2 == 1)
			return -count;
		else 
			return count;
	}
};

执行效率:
在这里插入图片描述

(二)改进暴力法

我们知道,左移一位等价于乘以2,那么可以使用这个技巧来减少加法的次数
设被除数为A,除数为B

  • 相等的情况:除数B经过x次左移后等于A,那么:
    A B = 2 x \frac {A}{B}=2^{x} BA=2x
    直接求得商
  • 不相等的情况:除数B经过x次左移后大于A,那么:
  • B ∗ 2 x &gt; A 并 且 B ∗ 2 x − 1 &lt; A B*2^{x}&gt;A并且B*2^{x-1}&lt;A B2x>AB2x1<A
    那么商是否为 2 x − 1 2^{x-1} 2x1呢?
    不是,这里只能说商的取值范围是 [ 2 x − 1 , 2 x ) [2^{x-1},2^{x}) [2x1,2x)
    还要再逐个相加去和被除数比对。

C++代码:

class Solution {
public:
	int divide(int dividend, int divisor) {
		if (dividend == INT32_MIN && divisor == -1)
			return INT32_MAX;

		long long count = 1;
		long long dividend_l = dividend, divisor_l = divisor;
		divisor_l = abs(divisor_l);
		dividend_l = abs(dividend_l);
		long long acc = divisor_l;

		if (divisor_l > dividend_l)
			return 0;

		int flag1 = (dividend > 0) ? 0 : 1;
		int flag2 = (divisor > 0) ? 0 : 1;

		while (acc < dividend_l) {
			if ((acc << 1) <= dividend_l) {
				acc <<= 1;
				count <<= 1;
			}
			else {
				acc += divisor_l;
				count++;
			}
		}
		if (acc > dividend_l)
			--count;

		if (flag1 + flag2 == 1)
			return -count;
		else
			return count;
	}
};

执行效率:
在这里插入图片描述
还是慢的一批
这是为什么呢?
原因在于:

while (acc < dividend_l) {
			if ((acc << 1) <= dividend_l) {
				acc <<= 1;
				count <<= 1;
			}
			else {
				acc += divisor_l;
				count++;
			}
		}

这里的else之后的代码效率太低,还是一个一个的加

(三)再次改进

之前说了(二)的缺点,于是对这一段进行改进,把这里也改成左移的方式计算:

while (acc < dividend_l) {
			if ((acc << 1) <= dividend_l) {
				acc <<= 1;
				count <<= 1;
			}
			else {
				count += divide(dividend_l - acc, divisor_l);
				break;
			}
		}

C++代码:

class Solution {
public:
	int divide(int dividend, int divisor) {
		if (dividend == INT32_MIN && divisor == -1)
			return INT32_MAX;

		long long count = 1;
		long long dividend_l = dividend, divisor_l = divisor;
		divisor_l = abs(divisor_l);
		dividend_l = abs(dividend_l);
		long long acc = divisor_l;

		if (divisor_l > dividend_l)
			return 0;

		int flag1 = (dividend > 0) ? 0 : 1;
		int flag2 = (divisor > 0) ? 0 : 1;

		while (acc < dividend_l) {
			if ((acc << 1) <= dividend_l) {
				acc <<= 1;
				count <<= 1;
			}
			else {
				count += divide(dividend_l - acc, divisor_l);
				break;
			}
		}
		if (acc > dividend_l)
			--count;

		if (flag1 + flag2 == 1)
			return -count;
		else
			return count;
	}
};

执行效率:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值