✅ LeetCode 29. 两数相除 — 思路总览
🧩 题目要求
给定两个整数 dividend
和 divisor
,实现 整数除法,不能使用乘法 *
、除法 /
和取余 %
运算符。
要求返回的结果应为 向零截断的整数商,即:
- 正数向下取整(如 8.3 → 8)
- 负数向上取整(如 -8.3 → -8)
如果商超出 int
范围(即 < -2³¹ 或 > 2³¹ - 1),返回 Integer.MAX_VALUE
。
📌 解题思路
1️⃣ 特殊情况处理
- 如果
dividend = Integer.MIN_VALUE
且divisor = -1
,结果将溢出,需返回Integer.MAX_VALUE
。
2️⃣ 记录结果正负号
- 用异或运算
(dividend < 0) ^ (divisor < 0)
判断结果是否为负数。 - 将除数和被除数都转成正数进行计算,最后再加上符号。
3️⃣ 使用 减法 + 位运算(左移) 模拟除法
- 使用 位移(
<<
)运算模拟乘法,通过将divisor
不断翻倍来逼近dividend
。 - 在每一轮中:
- 找出最大
divisor × 2^k
,使得该值不超过当前dividend
- 将该倍数加入到最终结果中
- 将
dividend
减去该倍数的值,继续下一轮
- 找出最大
4️⃣ 为什么使用 long?
- 避免
Math.abs(Integer.MIN_VALUE)
溢出问题 - 整个过程用
long
类型进行中间计算更安全,最后再强转为int
⏱️ 时间复杂度分析
- 每一轮减法都用 指数方式减少
dividend
,因此时间复杂度为:
O(log N),N 为 dividend 的绝对值
✅ 关键点总结
点位 | 说明 |
---|---|
🚫 不使用 * / % | 用减法和位移代替 |
⚠️ 特判溢出 | MIN_VALUE / -1 会溢出 |
📈 位运算加速 | 倍增 divisor 快速逼近 |
💡 先判断再左移 | (temp << 1) 防越界 |
🔒 使用 long 类型 | 防止中间计算溢出 |
java solution
class Solution {
public int divide(int dividend, int divisor) {
// 处理特殊溢出情况, 当被除数是-2^31且除数是-1时, 此时得到的结果会溢出
if(dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE;
}
// 记录结果正负
boolean negative = (dividend < 0) ^ (divisor < 0);
// 使用 long 转换避免溢出,并且将被除数和除数都转换成正数,
long ldividend = Math.abs((long) dividend);
long ldivisor = Math.abs((long) divisor);
int result = 0;
// 我们利用内层的while循环来快速找到不超过ldividend的最大的ldivisor * 2^k,
while(ldividend >= ldivisor) { //这里是大于等于, 因为如果被除数等于除数时,还能继续减
long temp = ldivisor;
int multiple = 1; //multiple是不超过ldividend的最大的ldivisor * 2^k中2^k的值
//之所以这里while循环判断条件里是(temp << 1)而不是ldividend > temp
//是因为如果是后者, 那么我们得到的最终的temp会超过ldividend
while(ldividend > (temp << 1)) {
temp <<= 1;
multiple <<= 1;
}
ldividend -= temp;
result += multiple;
}
return negative ? -result : result;
}
}