问题描述
分析
首先,题目要求我们只能使用32位有符号整数来存储,所以不能使用long
,long 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;
}
};