两数相除 图解题解
这道题到底在问什么
- dividend
- 43
- divisor
- 5
- 输出
- 8
先想最直接的笨办法
一刀切下 40,剩余从 43 直接掉到 3,商累加 8。一次抵笨办法的 8 次减法!(动画第 15 步)
最优解:一步一步想明白
- 3思路一句话:商就是被除数里装得下几个除数。先用最笨的一个个减打底,下面再用位运算倍增提速。下面一步步演给你看。
- 4减第 1 次把 5 一个个排开。减掉第 1 个,剩余从 43 变 38,计数 +1。
- 5继续一个个减再减一个 5,剩余 33,计数 2。一次只能减掉一个,效率低得很。
- 6还在慢慢减第 3 次,剩 28。照这速度还得再减 5 次,你已经能感觉到它有多笨了。
- 7剩 3 不够减就这么减 8 次才到剩 3(不够再减),商 = 8。可如果是 20 亿 ÷ 1 呢?要减 20 亿次——必然超时。
- 8核心提速:别一个个减。把除数不断翻倍(左移一位 = ×2),找出「不超过剩余的最大那一坨」一次性减掉,商也对应加 1、2、4、8…
- 95 << k 各代表的「倍数」把 5 一路左移:5、10、20、40。每个底下对应的「商」是 1、2、4、8。这就是我们的弹药库:能一次减 40,就等于一次顶了 8 个 5。
- 10temp=5,商片段=1正式开演。剩余 43,从最小的 temp=5 起步(对应商 1)。看 temp 能不能再翻倍而不超过剩余。
- 11temp 5→10,商片段 1→210 ≤ 43,可以!temp 翻到 10,商片段翻到 2。指针右移,继续试下一倍。
- 12temp 10→20,商片段 2→420 ≤ 43,继续翻!temp 到 20,商片段到 4。还能不能翻?再看下一格 40。
- 13temp 20→40,商片段 4→840 ≤ 43,还能翻!temp 到 40,商片段到 8。下一步要试 80——这次该到头了。
- 14停在 temp=40再翻就是 80,80 > 43 超了。停!当前 temp=40 就是「不超过 43 的最大一坨」,它一次顶 8 个 5。
- 15剩余 43→3,商 +8一刀切下 40,剩余从 43 直接掉到 3,商累加 8。一次抵笨办法的 8 次减法!
- 163 < 5,减不动回到最小的 5 重新起步,可剩余只有 3,连一个 5 都减不动。循环结束,这点零头(余数 3)直接丢掉(向零取整)。
- 1743 ÷ 5 = 8 ✓商累计 = 8,余 3 舍去。43 ÷ 5 = 8 ✓。整个过程只做了减法和左移,没碰一次乘除。
- 18temp=3,商片段=1换 10÷3 再走一遍,看外层可能要凑好几轮。剩余 10,从 temp=3 起步。
- 196≤10,翻!12>10 停3 翻倍到 6(≤10),再翻 12 超了停。一次减 6,剩 4,商 = 2。第 1 轮结束。
- 20剩 4,翻不动,减一个 3剩 4 还够减一个 3,但翻倍到 6 就超了,所以只减 3,剩 1,商累加到 3。剩 1 不够减,结束 → 10÷3 = 3。这就是外层 while 跑两轮的样子。
- 21商 = 3,余 1 丢弃两轮加起来商 = 3(2 + 1),余 1 丢掉。10 ÷ 3 = 3 ✓。可见外层 while 会反复「找最大坨→减掉」,直到剩余不够减。
- 22倍增过程只认正数。所以先记下「最终是正还是负」(两数异号→负),把两数都取绝对值算,算完再决定要不要加负号。
- 2332 位整数负数比正数多一个:能装 −2147483648,却装不下 +2147483648。所以 INT_MIN ÷ (−1) 必须特判,直接返回 INT_MAX。下面演这个雷。
- 24dividend = −2147483648看这个雷:被除数是 32 位能装的最小值 −2147483648,除数 −1。数学答案是把它变成正的。
- 25+2147483648 装不下理论商 +2147483648 比 32 位能装的最大值 还大 1。硬算会溢出回绕成负数,得到错的结果。
- 26直接返回 INT_MAX唯一会溢出的就这一种组合。代码开头一行特判:碰到它直接返回 INT_MAX = 2147483647,绕开溢出。其余情况都安全。
⚠️ 容易写错的地方
✗ 错:直接用 int 做翻倍 temp<<1
✓ 对:用 long(或先转 long)再左移
temp 翻倍可能瞬间超 int 上限,中途就溢出
✗ 错:忘了 INT_MIN ÷ (−1) 特判
✓ 对:开头一行特判返回 INT_MAX
理论商 2³¹ 超 32 位最大值,不特判必溢出
✗ 错:abs(INT_MIN) 直接取绝对值
✓ 对:先转 long 再取绝对值
int 范围内 abs(INT_MIN) 本身就溢出(没有 +2³¹)
完整代码(Python / C++ / Java)
Python
class Solution:
def divide(self, dividend, divisor):
INT_MIN, INT_MAX = -2147483648, 2147483647
if dividend == INT_MIN and divisor == -1:
return INT_MAX # 唯一溢出特判
neg = (dividend < 0) != (divisor < 0)
a, b = abs(dividend), abs(divisor)
q = 0
while a >= b:
temp, m = b, 1
while a >= (temp << 1): # 不断翻倍找最大坨
temp <<= 1
m <<= 1
a -= temp # 一次减掉一大坨
q += m
return -q if neg else qC++
class Solution {
public:
int divide(int dividend, int divisor) {
if (dividend == INT_MIN && divisor == -1) return INT_MAX;
bool neg = (dividend < 0) != (divisor < 0);
long a = labs((long)dividend), b = labs((long)divisor);
long q = 0;
while (a >= b) {
long temp = b, m = 1;
while (a >= (temp << 1)) { temp <<= 1; m <<= 1; }
a -= temp; q += m;
}
return neg ? -(int)q : (int)q;
}
};Java
class Solution {
public int divide(int dividend, int divisor) {
if (dividend == Integer.MIN_VALUE && divisor == -1)
return Integer.MAX_VALUE;
boolean neg = (dividend < 0) != (divisor < 0);
long a = Math.abs((long) dividend);
long b = Math.abs((long) divisor);
long q = 0;
while (a >= b) {
long temp = b, m = 1;
while (a >= (temp << 1)) { temp <<= 1; m <<= 1; }
a -= temp;
q += m;
}
return neg ? (int) -q : (int) q;
}
}复杂度
时间复杂度
O(log²n)
外层最多 log(商) 次;每次内层翻倍最多 log(商) 次 → O(log²n)。比笨办法的 O(n) 快出天际
空间复杂度
O(1)
只用了 temp / m / q 几个变量,没开额外结构
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 两数相除 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么倍增(左移)能代替乘法?+
左移一位等于 ×2。把除数 1、2、4、8 倍地翻,本质是用二进制凑出商,每次减掉「不超过剩余的最大 2 的幂倍」,商对应累加 2 的幂。
时间复杂度怎么算到 O(log²n)?+
外层每凑一段商,剩余至少减半,最多 log 次;每次内层翻倍也最多 log 次,相乘得 O(log²n)。
哪些情况会溢出,怎么处理?+
只有 INT_MIN÷(−1) 理论商超 INT_MAX,开头特判返回 INT_MAX;中途翻倍和取绝对值用 long 兜住。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 两数相除 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。