编程问题:
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
示例:
- 输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3 - 输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333…) = -2
解法:
1.倍增+递归
本题的精髓在于递归使用div函数进行商的求解。 首先,若除数大于被除数,则商起码是1;接着考虑商是否为2:考虑商是否为2,只需将原来的除数翻个倍,与被除数比较大小;倘若被除数比除数的两倍还多,则考虑继续增加商的值,这时不考虑商是否再线性增加——因为速度太慢,而是以指数级增加除数,即将除数再翻一个倍,如此循环下去… 当出现被除数小于除数时,可以感性地理解为2倍的除数过大,大过被除数,所以需要退一步,被除数减掉一倍的除数,让这个差值(可看成是近似二分后,较小的那一部分)与题目所给的最初始的除数进行div函数的计算,重复上述操作,直到在若干次二分后剩余的一部分小于除数,返回零,意味着递归的结束。将每一层函数计算所得的值相加,就得到了最终结果。这实际上就是二分法的思想。
时间复杂度O(log N)
空间复杂度O(1)
class Solution {
private:
int div(int a, int b) // a < 0 b < 0
{
if(a > b) return 0; // 递归退出条件
long count = 1;
long tb = b;
while(tb + tb >= a && tb + tb < 0){ // 当 -1073741824-1073741824 = 0 到-1073741824为止
tb += tb;
count += count;
}
return count + div(a-tb, b);
}
public:
int divide(int dividend, int divisor)
{
if(divisor == -1 && dividend == INT_MIN) return INT_MAX; // 溢出判断
int sign = 1; //商为正
if((dividend > 0 && divisor < 0)||(dividend < 0 && divisor > 0))
sign = -1; //商为负
//将被除数和除数设置为负数可以避免溢出 这里避免溢出体现在div函数中 tb + tb < 0 这个退出循环条件
int dividend_neg = dividend>0 ? -dividend : dividend;
int divisor_neg = divisor>0 ? -divisor : divisor;
if(dividend_neg > divisor_neg) return 0;
long res = div(dividend_neg, divisor_neg);
return sign == -1 ? -res : res;
}
};