LeetCode-29-两数相除
in LeetCode with 2 comment

LeetCode-29-两数相除

in LeetCode with 2 comment

原题地址:两数相除

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

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

示例 1:

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

示例 2:

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

说明:

除法转为减法

当被除数大于等于除数时,被除数就减去除数,然后商就加一,一直循环直到被除数小于除数时就可以得到结果:

/**
 * @param {number} dividend
 * @param {number} divisor
 * @return {number}
 */
const divide1 = function(dividend, divisor) {
    // 由于负数的存储范围比正数大1,所以最小的负数除以-1时就会溢出
    if (dividend === -0x80000000 && divisor === -1) {
        return 0x7fffffff
    }
    let isNegative = (dividend ^ divisor) < 0; // 记录符号
    // 都转为正数
    dividend = Math.abs(dividend); //
    divisor = Math.abs(divisor);
    // 转为减法运算并记录运算次数
    let count = 0;
    while (dividend >= divisor) {
        dividend -= divisor;
        count ++;
    }
    return isNegative ? -count : count;
};

测试:

let start = new Date();
const test = divide1;
console.log(test(10, 3)); // 3
console.log(test(7, -3)); // -2
console.log(new Date().getTime() - start.getTime()); // 5

时间复杂度: 时间复杂度为O(n),n就是被除数除以除数得到的商
空间复杂度: 常数级空间复杂度O(1)

位移运算

上述算法的运行效率相当低,特别时当被除数绝对值特别大而除数绝对值又特别小的时候,所以该算法提交到LeetCode时会超时。

题目要求我们不能使用乘法和除法操作,但我们可以用位移运算来代替乘法和除法操作。我们可以先把$dividend$除以$2^i$,$i$从$31$开始依次减小,直到我们找到一个数$i$,使得 $dividend\div2^i\geq divisor$,证明我们找到了一个足够大的数$i$,使得 $2^i \times divisor \leq dividend$。然后我们就可以让 $dividend$ 减去这个数,并对剩下的数继续同样的操作。例如我们以$100 \div 3$为例,我们从$31$开始尝试,发现能找到的最大的符合条件的$i$为$5$,$100 \div 2^5 = 100 \div 32 = 3$,刚好满足大于等于$3$的条件,$100 - 2^5 \times 3 = 4$,即$100$减去了$32$个$3$,然后我们对$4$进行同样的操作,可以再减去一个$3$,总共减去了$33$个$3$,所以最终的结果为$33$。

具体实现如下:

/**
 * @param {number} dividend
 * @param {number} divisor
 * @return {number}
 */
const divide2 = function(dividend, divisor) {
    // 由于负数的存储范围比正数大1,所以最小的负数除以-1时就会溢出
    if (dividend === -0x80000000 && divisor === -1) {
        return 0x7fffffff
    }
    let isNegative = (dividend ^ divisor) < 0; // 记录符号
    // 都转为正数
    dividend = Math.abs(dividend);
    divisor = Math.abs(divisor);
    let result = 0;
    for (let i = 32; i >= 0; i --) {
        if ((dividend>>>i) >= divisor) { // dividend/2^i >= divisor,注意使用无符号位移
            result += (1<<i); // result += 2^i
            dividend -= (divisor<<i); // dividend -= divisor * 2^i
        }
    }
    return isNegative ? -result : result;
};

测试:

let start = new Date();
const test = divide2;
console.log(test(10, 3)); // 3
console.log(test(7, -3)); // -2
console.log(test(-2147483649, 1)); // -2147483649
console.log(new Date().getTime() - start.getTime()); // 8

时间复杂度: 最多只需要32次循环,所以时间复杂度为常数级,$O(1)$
空间复杂度: 常数级额外空间,空间复杂度为$O(1)$