leetcode每日一题系列-两数相除-「加法模拟乘法+二分查找」-「位运算」

249 阅读2分钟

leetcode-29-两数除法

[博客链接]

菜🐔的学习之路

掘金首页

[题目链接]

题目链接

[github地址]

github地址

[题目描述]

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

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

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2

 

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333..) = truncate(3) = 3

示例 2:

输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333..) = -2

提示:

  • 被除数和除数均为 32 位有符号整数。
  • 除数不为 0。
  • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [231, 231 1][−2^{31},  2^{31} − 1]。本题中,如果除法结果溢出,则返回231 12^{31} − 1

思路一:加法模拟乘法+二分查找

  • 因为存在越界情况,同时不能存储long型字符
  • 所以需要对于边界直接判断结果
  • Z * X < Y < (Z+1) * X
  • 通过二分查找不断逼近结果
public int divide(int dividend, int divisor) {
    int res = 0;
    if (dividend == 0) {
        return 0;
    }

    boolean flag = (dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0);
    if (dividend == Integer.MIN_VALUE && divisor == -1) {
        return Integer.MAX_VALUE;
    }
    if (divisor == Integer.MIN_VALUE) {
        return dividend == Integer.MIN_VALUE ? 1 : 0;
    }
    long _dividend = Math.abs((long) dividend);
    long _divisor = Math.abs((long) divisor);
    long l = 0, r = _dividend;
    while (l < r) {
        long mid = l + r + 1 >> 1;
        if (mul(mid, _divisor) <= _dividend) {
            l = mid;
        }
        else {
            r = mid - 1;
        }
    }
    r = flag ? -r : r;
    if (r > Integer.MAX_VALUE || r < -Integer.MAX_VALUE - 1) return Integer.MAX_VALUE;
    return (int)r;

}

long mul(long a, long k) {
    long ans = 0;
    while (k > 0) {
        if ((k & 1) == 1) ans += a;
        k >>= 1;
        a += a;
    }
    return ans;
}
  • 时间复杂度O(lgn)
  • 空间复杂度O(1)

位运算

  • 通过移位操作来计算除法是CSAPP这本书的例题
  • 再次推荐
  • 同时通过异或可以直接判断正负
public static int divide(int dividend, int divisor) { 
    if (dividend == 0)return 0;
    if (dividend == Integer.MIN_VALUE && divisor == -1) {
        return Integer.MAX_VALUE; 
    } 
    boolean negative; 
    //用异或来计算是否符号相异
    negative = (dividend ^ divisor) <0; 
    long t = Math.abs((long) dividend); 
    long d= Math.abs((long) divisor); 
    int result = 0; 
    for (int i = 31; i >= 0; i--){ 
        if (t >> i >= d){ 
        //将结果加上2^n
        result+=1<<i; 
        //将被除数减去2^n*divisor 
        t-=d<<i;
        } 
     }
     return negative ? -result : result;//符号相异取反
 }
  • 时间复杂度O(lgn)
  • 空间复杂度O(1)