两数相除

试题来源:力扣(英文)力扣(中文)LintCodegeeksforgeeks

将两个整数相除,要求不使用乘法、除法和 mod 运算符。如果溢出(超出32位有符号整型表示范围),返回 Integer.MAX_VALUE。

Input:a = 10, b= 3
Output: 3

问题分析:

这题让计算两个数的商,但不能使用乘法和除法。我们可以考虑使用减法,用被除数不断的减去除数,直到不够减为止。如果每次只减去一个除数,这样效率明显很差,我们可以考虑减去除数的倍数。假如这两个数的商是 X ,这个 X 在二进制中有些位置是 0 有些位置是 1 。我们每次只需要减去位置是 1 的数字和除数的乘积即可。

因为被除数和除数可能符号不一致,我们这里统一把它们都转换为负数,然后在相减。这里能不能都转为正数呢,实际上是不行的,因为在 int 范围内,负数的范围要比正数的范围大[-2147483648,2147483647],如果负数是 -2147483648 ,把它转为正数会出现溢出。

这里关键点是怎么确定减去除数的多少倍?我们只需要尝试把除数往左移一位,判断是否大于被除数(这里被除数和除数都转为负数了),如果大于被除数就往左移一位,来看下代码。

public int divide(int dividend, int divisor) {
    boolean negative = dividend < 0 ^ divisor > 0; // 结果是否是负数。
    // 为了方便计算,被除数和除数都转成负数。
    dividend = dividend < 0 ? dividend : -dividend;
    divisor = divisor < 0 ? divisor : -divisor;
    int res = 0;// 两数相除的商。
    // 被除数不能小于minDiv,否则往左移的时候会出现溢出。
    int minDiv = Integer.MIN_VALUE >> 1;
    // 被除数和除数都是负数。
    while (dividend <= divisor) {
        int tryDiv = divisor;// 用被除数减去除数的倍数。
        int times = 1;// 记录减去的倍数。
        // 尝试减去除数的倍数。
        while (tryDiv >= minDiv && dividend <= tryDiv << 1) {
            tryDiv <<= 1;
            times <<= 1;// 被除数的倍数。
        }
        dividend -= tryDiv;
        res -= times;
    }
    // 如果结果溢出,直接返回int范围内的最大值。
    if (negative && res == Integer.MIN_VALUE)
        return Integer.MAX_VALUE;
    return negative ? -res : res;
}

信奥赛编程(刷题请进)>>>

经过两年的打磨,我的新作《算法秘籍》已经出版,有需要的可以点击购买。也可以点击 内容介绍 查看详情。