题目来源
题目描述
class Solution {
public:
int divide(int dividend, int divisor) {
}
};
题目解析
分析
- 题目要求不能用乘法、除法和mod。那么只能用加法、减法、或者位运算了。要实现两数相除的功能我们可以用减法、位运算。这里用减法
- 题目说了,除数disisor和被除数dividend可能是正数、负数
- 为简化代码和逻辑,我们可以先把除数disisor和被除数dividend都转换成正正或者负负,再进行统一的逻辑处理,同时记录下最终结果是正号还是负号。
- 如果均转换为正正,那就有可能溢出(INT_MIN转换为INT_MAX时),但是把任意正整数转换为负数是不会溢出的。因此我们把disisor和dividend均转成负数
class Solution {
/**
* 使用减法实现两个负整数的相除操作
*
* dividend:被除数
* divisor:除数
*/
int divideCore(int dividend, int divisor) {
}
public:
/**
* 两数相除
*
* dividend 被除数
* divisor 除数
*/
int divide(int dividend, int divisor) {
// 处理整型溢出问题,-2³¹=0x80000000
if (dividend == 0x80000000 && divisor == -1) {
return INT32_MAX;
}
// 用来记录除数和被除数是负整数的个数,初始化为2
int negative = 2;
// 如果被除数是正整数,则转换成对应的负整数
if (dividend > 0) {
negative--;
dividend = -dividend;
}
// 如果除数是正整数,则转换成对应的负整数
if (divisor > 0) {
negative--;
divisor = -divisor;
}
int result = divideCore(dividend, divisor);
return negative == 1 ? -result : result;
}
};
我们接下来继续分析两个负整数相除的处理,以 dividend/divisor 为例子,当 dividend 小于等于 divisor 时,我们循环执行 (dividend-divisor) 并计数:
/**
* 使用减法实现两个负整数的相除操作
*
* dividend:被除数
* divisor:除数
*/
int divideCore(int dividend, int divisor) {
int quotient = 0;
while (dividend <= divisor){
quotient++;
dividend -= divisor;
}
return quotient;
}
但是有一个问题,当 dividend 远小于 divisor 时,这段代码执行会花费太长时间,导致超时。
因此,我们得想其他改进的方法,整体思路还是通过减法实现,但为了缩小想减的次数,我们借鉴二分法:
- 当被除数 dividend 小于除数 divisor 时,继续比较判断dividend 是否小于divisor 的 2 倍,如果是,则继续判断dividend 是否小于divisor 的 4 倍、8 倍等
- 如果dividend 最多小于divisor 的 2ᵏ 倍,那么将被除数减去除数的 2ᵏ 倍,然后将剩余的被除数重复前面的步骤
/**
* 使用减法实现两个负整数的相除操作
*
* dividend:被除数
* divisor:除数
*/
int divideCore(int dividend, int divisor) {
int ans = 0;
while (dividend <= divisor){
int quo = 1;
int value = divisor;
// 注意题目要求,只能使用加法和减法
// 因此,通过value+value实现value*2的效果
while (dividend <= value + value){
quo += quo;
value += value;
}
ans += quo;
dividend -= value;
}
return ans;
}
- 但是value + value可能溢出,所以我们需要限制一下value的大小,val是负数,而最小的 int 负数是 -2³¹(0x80000000),它的一半是 -2³¹/2=-2³⁰(0xc0000000)
/**
* 使用减法实现两个负整数的相除操作
*
* dividend:被除数
* divisor:除数
*/
int divideCore(int dividend, int divisor) {
int ans = 0;
while (dividend <= divisor){
int quo = 1;
int value = divisor;
// 注意题目要求,只能使用加法和减法
// 因此,通过value+value实现value*2的效果
while (value >= 0xc0000000 && dividend <= value + value){
quo += quo;
value += value;
}
ans += quo;
dividend -= value;
}
return ans;
}
位运算
class Solution {
public:
int divide(int dividend, int divisor) {
long m = labs(dividend), n = labs(divisor), res = 0;
if (m < n) return 0;
long t = n, p = 1; // p = 1结构至少是1
while (m > (t << 1)) {
t <<= 1; // 最小解翻倍
p <<= 1; // 当前测试的值也翻倍
}
res += p + divide(m - t, n);
if ((dividend < 0) ^ (divisor < 0)) res = -res;
return res > INT_MAX ? INT_MAX : res;
}
};
11 除以 3
- 首先11比3大,结果至少是1
- 然后让3翻倍,就是6,发现11比3翻倍后还要大,那么结果至少是2了
- 这个6再翻倍,得12,11不比12大