leetcode刷题日记-【29. 两数相除】

171 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

题目描述

给定两个整数,被除数 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]。本题中,如果除法结果溢出,则- - 返回 231 − 1。

题目元素

  • 给定一个被除数一个除数,它们的范围都是32位有效整数,包括整数负数0。
  • 在不使用乘法、除法、模运算等求出这两个数的商,商值的绝对值向下取整。

解题思路

  • 求两个数的商,不使用乘除法及模运算,首先想到的就是对被除数进行加减操作。而对于整数和2的乘法可以替换成这个整数带符号左移一位,对于整数和2的除法可以替换成这个整数带符号右移一位。
  • 被除数和除数可能为正数也可能为负数,所以要涉及到整数的正负判断,二进制的最高位是符号位,所以可以对整数带符号右移31位,剩下的一位就是符号位,来判断该数是正数还是负数。
  • 正整数的绝对值要比负数的绝对值小1,所以可以将被除数和除数转成负数进行操作,避免溢出。
  • 可以采用每次 减去除数的二倍。。。四倍。。。八倍。。。直到剩下的差小于当前这个数,再依次将这个临界数除以2。。。直到差小于最原始的除数。即得到解

代码实现

public static int divide(int dividend, int divisor) {
    // 当被除数为0直接返回0
    if (dividend == 0) {
        return 0;
    }
    // 标志最终值是否需要取反,true需要取反
    boolean revertFlag = false;
    // 将两个数转成负数
    if (((dividend >>> 31) & 1) == 0) {
        // 正数
        revertFlag = true;
        dividend = -dividend;
    }
    if (((divisor >>> 31) & 1) == 0) {
        revertFlag = !revertFlag;
        divisor = - divisor;
    }
    // 除数为1或-1
    if (divisor == -1) {
        if (dividend == Integer.MIN_VALUE) {
            return revertFlag ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        }else {
            return revertFlag ? dividend : -dividend;
        }
    }
    // 依次减原始除数的二倍
    // 当前被除数剩余未减的数
    int last = dividend;
    // 当前增加到的除数的倍数
    int nowSubtract = divisor;
    // 增大到nowSubtract时对除数的倍数
    int end = 1;
    // 已经对被除数扣减过的除数的倍数
    int ans = 0;
    // 注意这里必须要加上nowSubtract大于最小值的一半,不加上的话它的2倍就会溢出,变成一个正数,导致计算错误
    while (last <= nowSubtract && nowSubtract > Integer.MIN_VALUE >> 1) {
        last -= nowSubtract;
        ans  += end;
        nowSubtract <<= 1;
        // 记录当前已经扣减的次数
        end <<= 1;
    }
    // 剩下的被除数已经小于当前的除数
    while (last <= divisor) {
        // 只有当增大过nowSubtract才能对它做缩小操作,否则就变成除数的几分之一了
        if (end > 1) {
            end >>= 1;
            nowSubtract >>= 1;
        }
        // 一直缩小到小于剩余被除数
        if (nowSubtract < last){
            continue;
        }
        // 扣减
        last -= nowSubtract;
        // 扣减次数累加
        ans += end;
    }
    // 注意如果要转成正数,需要注意是否溢出
    if (!revertFlag && ans == Integer.MIN_VALUE) {
        return Integer.MAX_VALUE;
    }
    return revertFlag ? -ans : ans;
}