[leetcode 29],两数相除(不使用乘,除,mod运算符)

问题描述

https://leetcode-cn.com/problems/divide-two-integers/

在这里插入图片描述

分析

首先,题目要求我们只能使用32位有符号整数来存储,所以不能使用longlong long来进行存储

其次,测试用例中是存在一些边界条件的,int类型的数据范围为[-2147483628,2147483647],如果我们贸然对-INT_MIN转为正数,那么是会越界的。

另外,是可以考虑将除数与被除数都转为负数,那么就不必考虑越界的问题。但是,在C++中,负数不能进行左移操作
在这里插入图片描述

需要移位操作的原因

while(dividend >= divisor) {
	res ++;
    dividend -= divisor;
}

我们这样做,他的时间复杂度为 O ( n ) O(n) O(n),在提交之后是会被判断为超时,所以我们需要用位运算进行优化,将时间复杂度降为 O ( l o g n ) O(log n) O(logn)
在这里插入图片描述

使用快速幂的思想(指数递增),在每次减法循环的时候将除数增大一倍来加速减法的效率,对应的倍数每次也增大一倍

但是我们在倍增的时候,要防止因为左移运算产生的越界问题,也就是说,我们每次倍增后的结果不能超过INT_MAX,我就要保证倍增之前的数据是在[0,INT_MAX / 2]之间。

并且倍增后的数据,不能超过除数

while(dividend >= divisor) {
	int tmp = divisor;
    int cnt = 1;

    // INT_MAX = 2147483647 防止左移越界
    while(tmp < (INT_MAX >> 2) && (tmp << 1) <= dividend) {
    	tmp <<= 1;
        cnt <<= 1;
    }

    res += cnt;
    dividend -= tmp;
}

预处理

这是我们在执行上述代码之前需要做的一步,就是将除数和被除数都变为正数,这个时候就需要判断边界了和结果的正负号问题了

对于结果的正负号问题,我们可以设置一个标记位,并使用^异或运算来判断,同则为零,异则为一

bool flag = (dividend > 0) ^ (divisor > 0);

对于除数为INT_MIN时,因为2147482648大于所有int类型的数,所以说结果为0

其次,当被除数为INT_MIN时,我们将被除数变为正数,可以先给被除数 + + +一个除数,然后再变为正数

最后加上一些边界的处理就可,别问为什么要加,wa过

if(divisor == 1) return dividend;
if(divisor == -1) {
	if(dividend != INT_MIN) return -dividend;
		return INT_MAX;     // 变为 int 型数据的最大值
}
if(dividend == divisor) return 1;
if(divisor == INT_MIN) return 0;

这里要注意一个点就是
在这里插入图片描述
结果会变为int型数据的最大值

ac程序

class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor == 1) return dividend;
        if(divisor == -1) {
            if(dividend != INT_MIN) return -dividend;
            return INT_MAX;     // 变为 int 型数据的最大值
        }
        if(dividend == divisor) return 1;
        if(divisor == INT_MIN) return 0;
        int res = 0;
        // 最后结果的符号
        bool flag = (dividend > 0) ^ (divisor > 0);
        // 排除 INT_MIN 转为 INT_MAX 后,将负数转为正数
        divisor = abs(divisor);
        if(dividend == INT_MIN) {
            res++;
            dividend += divisor;
        }
        dividend = abs(dividend);   

        while(dividend >= divisor) {
            int tmp = divisor;
            int cnt = 1;

            // INT_MAX = 2147483647 按照整除的思想
            while(tmp < (INT_MAX >> 2) && (tmp << 1) <= dividend) {
                tmp <<= 1;
                cnt <<= 1;
            }

            res += cnt;
            dividend -= tmp;
        }

        return flag ? -res : res;
    }
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值