一、题目要求
给定两个整数 dividend(被除数)和 divisor(除数),要求计算:
dividend / divisor
并返回向 0 截断后的整数结果。
题目有三个关键限制:
- 不能使用
*、/、%运算符 - 结果需要考虑 32 位有符号整数范围
- 如果结果溢出,返回
Integer.MAX_VALUE
典型示例:
输入:dividend = 10, divisor = 3
输出:3
输入:dividend = 7, divisor = -3
输出:-2
二、解题思路总览
这道题的难点并不在“除法本身”,而在三个地方:
- 不能用除法
- 不能溢出
- 不能超时
整体思路可以拆成三步:
1. 特殊溢出情况提前处理
Integer.MIN_VALUE / -1
数学结果是 2147483648,已经超过 int 最大值,必须单独处理。
2. 统一使用负数进行计算
原因只有一个:
Integer.MIN_VALUE 没有对应的正数,但负数范围更大,能避免溢出。
3. 用「倍增减法」模拟除法
核心思想:
-
除法本质是:被除数里能减去多少个除数
-
与其一次减一个,不如:
- 每次把除数翻倍
- 一次性减掉最大的那一块
-
类似二进制拆分,时间复杂度从
O(n)优化到O(log n)
三、完整代码
class Solution {
public int divide(int dividend, int divisor) {
// 1. 处理溢出情况
if (dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE;
}
// 2. 判断结果符号
boolean negative = (dividend < 0) ^ (divisor < 0);
// 3. 统一转为负数,避免溢出
int a = dividend > 0 ? -dividend : dividend;
int b = divisor > 0 ? -divisor : divisor;
int result = 0;
// 4. 倍增减法
while (a <= b) {
int temp = b;
int count = 1;
// 尝试不断翻倍
while (temp >= (Integer.MIN_VALUE >> 1) && a <= temp + temp) {
temp += temp;
count += count;
}
a -= temp;
result += count;
}
return negative ? -result : result;
}
}
四、逐行分析代码在做什么
1. 为什么要先判断这一句?
if (dividend == Integer.MIN_VALUE && divisor == -1) {
return Integer.MAX_VALUE;
}
因为:
Integer.MIN_VALUE = -2147483648-2147483648 / -1 = 2147483648- 超过
int能表示的最大值
这是唯一一个必然溢出的情况,必须提前返回。
2. 这一句在判断什么?
boolean negative = (dividend < 0) ^ (divisor < 0);
^ 是异或运算:
- 一个正,一个负 → 结果为负
- 同为正或同为负 → 结果为正
这里只是记录最终结果的符号,不参与具体计算。
3. 为什么全部转成负数?
int a = dividend > 0 ? -dividend : dividend;
int b = divisor > 0 ? -divisor : divisor;
原因很关键:
Integer.MIN_VALUE不能取相反数- 负数区间是
[-2147483648, -1],比正数多一个 - 用负数可以保证整个计算过程不溢出
从这一行开始:
a表示被除数(负数)b表示除数(负数)
4. 外层 while:为什么是 a <= b?
while (a <= b) {
注意:现在全是负数
a <= b等价于:|a| >= |b|- 说明被除数还至少能减去一个除数
5. temp 和 count 是干什么的?
int temp = b;
int count = 1;
含义:
temp:当前这一轮要减掉的「除数倍数」count:对应这次减法,商要加多少
初始状态表示:
减掉 1 个 divisor
商增加 1
6. 这层 while 是整道题的核心
while (temp >= (Integer.MIN_VALUE >> 1) && a <= temp + temp) {
temp += temp;
count += count;
}
逐个条件解释:
temp >= (Integer.MIN_VALUE >> 1)
Integer.MIN_VALUE >> 1 = -1073741824- 这一步是 防止
temp + temp溢出 - 确保下一次翻倍仍在 int 范围内
a <= temp + temp
- 判断被除数还能不能减掉「翻倍后的 temp」
- 如果能,就继续扩大这一块
翻倍本身:
temp += temp; // 除数 × 2
count += count; // 商 × 2
这是典型的倍增思想,每次把能减掉的最大块找出来。
7. 真正“做除法”的地方
a -= temp;
result += count;
含义非常直观:
- 从被除数中减掉这一大块
- 商累加对应的倍数
8. 最后恢复符号
return negative ? -result : result;
前面一直在算「绝对值意义下的商」,这里统一处理正负。
五、总结
这道题本质考察的不是除法,而是三种能力:
-
边界意识
- 明确
Integer.MIN_VALUE的特殊性
- 明确
-
数学等价转换
- 除法 → 减法 → 倍增减法
-
二进制与溢出控制
>> 1的真正含义- 为什么要统一用负数
当你真正理解这道题时,你学到的不是一个技巧,而是:
在强约束条件下,如何把一个问题“拆解、转化、再重组”。
这也是这道题成为经典面试题的原因。