题目:
给你两个整数,被除数 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
}