每天学习一点算法 2026/04/13
题目:两数相除
给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。
返回被除数 dividend 除以除数 divisor 得到的 商 。
注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231, 231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。
这道题可给我折磨坏了,其实最简单的办法就是用 dividend 一直减 divisor ,直到结果小于 divisor,减的次数就是我们要求的结果。
typescript
function divide(dividend: number, divisor: number): number {
let res = 0
while (dividend >= divisor) {
dividend -= divisor
res++
}
return res
};
但是不用想肯定是超时的,这个替换一个思路其实是要在 [1, dividend] 的范围内找到一个数字 n,使他的 dividend - divisor < divisor × n <= dividend 。
这样我们就是可以利用二分查找,减少复杂度,但是求倍数又是一个问题了,官方建议用使用 快速乘 类似 Pow(x, n) 的 快速幂,像这样:
typescript
function helper (x: number, n: number) {
// 较小的数作为倍数
if (x < n) {
[x, n] = [n, x]
}
if (n === 1) {
return x
}
if (x === 1) {
return n
}
const m = n >> 1 // = Math.floor(n / 2) 利用右移代替除二
if (m + m === n) {
// 偶数
return helper(x, m) + helper(x, m)
} else {
// 奇数
return helper(x, m) + helper(x, m) + x
}
}
但是遇到 dividend = 1026117192 divisor = -874002063 这种还是会超时,欺骗我感情。
最后呢,我们还是利用累加来判断 x × n 是否大于 dividend 来实现
typescript
function divide(dividend: number, divisor: number): number {
if (dividend === 0) return 0
const MAX = 2147483647
const MIN = -2147483648
let res = 0
let flag = (divisor > 0 && dividend > 0) || (divisor < 0 && dividend < 0) // 正负标识
// 取绝对值做计算可以减少很多条件判断
dividend = Math.abs(dividend)
divisor = Math.abs(divisor)
if (dividend < divisor) return 0 // 除数大于被除数直接返回 0
if (divisor === 1) {
// 除数是 1 结果直接等于被除数
res = dividend
}
if (dividend === divisor) {
// 被除数和除数相等直接结果为1
res = 1
}
// 辅助函数用于判断 x * n 是否大于 dividend
function helper (x: number, n: number) {
// 较小的数作为倍数
if (x < n) {
[x, n] = [n, x]
}
let sum = 0
// 累加判断结果是否大于 dividend
while (n > 0) {
sum += x
if (sum > dividend) {
return false
}
n--
}
return true
}
// 二分查找目标倍数
let left = 1, right = dividend
while (left <= right && divisor > 1 && dividend !== divisor) {
const mid = left + ((right - left) >> 1) // 利用右移代替除二
const chenji = helper(divisor, mid)
if (!chenji) {
// divisor的mid倍大于dividend
right = mid - 1
} else {
// divisor的mid倍小于等于dividend
left = mid + 1
res = mid
}
}
// 最后根据符号返回对应结果,注意边界判断
if (flag) {
return Math.min(MAX, res)
} else {
return Math.max(MIN, -res)
}
};
题目来源:力扣(LeetCode)