29两数相除(增倍除数法)

1、题目描述

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

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

2、示例

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

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

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

3、题解

如果单靠被除数慢慢减除数,一直减到被除数小于除数,这种方法时间复杂度为O(n)超时。用增倍除数的方法,举个例子如果被除数16,除数3,用我们上面的方法要遍历5次,接下来,我们使用不断增倍除数,比如:

被除数 除数

16 3

13 6

7 12

发现除数大于被除数,除数还原为原始值重新开始

7 3

4 3

1 3

虽然这个例子遍历次数相等,对于较大的数,可以减少时间复杂度。时间复杂度为O(logn)。
除此之外,还要将dividend和divisor都转换为负数计算,因为负数最小值是-pow(2,31)比正数最大值是pow(2,31)-1范围要大,如果转为正数计算-pow(2,31)转不了pow(2,31)。特殊考虑两种情况-pow(2, 31)除以-1等于pow(2, 31)超过整数最大范围溢出,返回整数最大值;-pow(2, 31)除以1等于-pow(2, 31),因为转为负数计算-pow(2, 31)除以-1等于pow(2, 31)溢出。

  • 不断循环,被除数减mutiple*除数,商+=multiple,一直减到dividend>divisor_,此时dividend为余数
  • multiple记录divisor的倍数,通过dividend减multiple倍的divisor,商ret+=multiple,商等于所有除数的倍数之和
  • 当divisor<=dividend,dividend不断减除数,除数翻倍,multiple翻倍
  • 当divisor>dividend,divisor还原为原始值divisor_,multiple为divisor的倍数也初始为1
#include<iostream>
#include<math.h>
using namespace std;
class Solution {
public:
    int divide(int dividend, int divisor) {
		//简化版本:增倍除数法
        long res=0,a=abs(dividend),b=abs(divisor);        
        while(a>=b){
            long cnt=1,base=b;
            while(a>=base)
			{
				res+=cnt;
				a-=base;
				cnt<<=1;
				base<<=1;
			}	
        }
        res=((dividend<0)^(divisor<0))?-res:res;
        return (INT_MAX<res||res<INT_MIN)?INT_MAX:res;
    }
};
class Solution1 {
public:
	int divide(int dividend, int divisor) {
		int ret = 0;  //返回的结果值商
		int flag = -1;
		//对最后的商符号判断flag=1商为正数,flag=-1商为负数
		if (dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0)
			flag = 1;
		//-pow(2, 31)除以-1等于pow(2, 31)超过整数最大范围溢出,返回整数最大值
		if (dividend == -pow(2, 31) && divisor == -1)
			return pow(2, 31) - 1;
		//-pow(2, 31)除以1等于-pow(2, 31),因为转为负数计算-pow(2, 31)除以-1等于pow(2, 31)溢出
		if (dividend == -pow(2, 31) && divisor == 1)
			return -pow(2, 31);	
		//将dividend和divisor都转换为负数计算,因为负数最小值是-pow(2,31)比正数最大值是pow(2,31)-1范围要大,如果转为正数计算-pow(2,31)转不了pow(2,31)
		if (dividend > 0)
			dividend = -dividend;
		if (divisor > 0)
			divisor = -divisor;
		//记录原始divisor,因为divisor在计算过程中不断翻倍,当divisor>dividend,divisor还原为原始值divisor_
		int divisor_ = divisor;
		//如果dividend>divisor,商为0
		if (dividend > divisor)
			return 0;
		//multiple记录divisor的倍数,通过dividend减multiple倍的divisor,商ret+=multiple
		int multiple = 1;
		//不断循环,一直减到dividend>divisor_,此时dividend为余数
		while (dividend <= divisor_)
		{
			if (dividend <= divisor)
			{
				dividend = dividend - divisor;
				ret += multiple;  //商等于所有除数的倍数之和
				//防止溢出,-1100540749除以-1090366779,除数翻倍就会溢出				
				if (divisor < -pow(2, 30))
					divisor = -pow(2, 31);
				else
					divisor += divisor;  //除数翻倍,可以divisor<<=1左移一位数值翻倍
				multiple += multiple;  //除数翻倍,multiple为除数的倍数,也翻倍,可以multiple<<=1左移一位数值翻倍
				
			}
			//当divisor>dividend,divisor还原为原始值divisor_,multiple为divisor的倍数也初始为1
			else
			{
				divisor = divisor_;
				multiple = 1;
			}
		}
		//flag=1商为正数,flag=-1商为负数
		if (flag == 1)
			return ret;
		else
			return -ret;
	}
};
int main()
{
	int dividend = -pow(2,31);
	int divisor = 2;
	Solution solute;
	cout << solute.divide(dividend, divisor) << endl;
	return 0;
}

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值