29. 两数相除

120 阅读2分钟

题目:
给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。

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

注意: 假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231,  231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。

算法:
这个题目本质上可以理解为,被除数能减去多少个除数
方法一:二分 + 倍增乘法 (不限制使用int64)

倍增乘法:a * b 意味着b个a相加,假设b=5吧, 转换为二进制:1(加4个a)001(加一个a),总共加5个a。所以我们每次检查b的最低为是否为1,是则ans = ans + a,但是b的二进制表示中,不同位置应该加的a是不一样的,每个高位的a是低位表示的a两倍。我们每次对a进行倍增就好了。 详见mul方法

func divide(dividend int, divisor int) int {
    flag := 1
    if (dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0) {
        flag = -1
    }
    a, b := dividend, divisor
    if a < 0 {
         a = -a
    }
    if b < 0 {
         b = -b
    }
    left, right := 0, a 
    for left < right {
        mid := (left + right + 1) >> 1
        if mul(mid, b) > a {
            right = mid - 1
        } else {
            left = mid
        }
    }
    if flag == -1 {
        left = -left
    }
    if left < math.MinInt32 {
        return math.MinInt32
    }
    if left > math.MaxInt32 {
        return math.MaxInt32
    }
    return left
}

func mul(a, b int) int {
    ans := 0 
    for b > 0 {
        if b & 1 == 1 {
            ans = ans + a
        }
        b = b >> 1
        a = a + a
    }
    return ans
}

方法二:二分 + 倍增乘法 (限制使用int64) 方法一中实际上使用了int64数据类型,只是限制了数据范围在int32, 严格来说只能使用数据类型int32, 因此不能使用left < math.MinInt32这种判断。 因为负数比整数多一个,我们注意math.MaxInt32 变成最大正数的情况即可。

func divide(dividend int, divisor int) int {
    if dividend == math.MinInt32 && divisor == -1 {
        return math.MaxInt32
    }

    postiveFlag := true
    if (dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0) {
        postiveFlag = false
    }
    if dividend > 0 {
        dividend = -dividend
    }
    if divisor > 0 {
        divisor = -divisor
    }
    ans := 0
    // 被除数dividend > divisor直接返回0
    for dividend <= divisor {
        tmp := divisor
        count := 1
        // 被除数减去除数大于除数,意味着已经不够一个除数了
        for dividend - tmp <= tmp {
            tmp = tmp + tmp
            count = count + count
        }
        // fmt.Println(dividend, tmp, dividend - tmp, count)
        // dividend减去divisor最大的2^x次幂
        // 被除数能减去多少个除数
        dividend = dividend - tmp
        ans = ans + count
    }
    if !postiveFlag {
        return -ans
    }
    return ans
}