题目描述
思路解析动画文字版
最朴素的做法,从 0 往上枚举,算 1 的平方、2 的平方、3 的平方,直到平方超过 x,停在前一个。能算对,但 x 很大时,比如二十亿,要试到四万多次,太慢。
为什么能二分:候选答案 m 越大,它的平方也越大,这是单调的。所以可以在区间 0 到 x 上对 m 二分。若 m×m 不超过 x,这个 m 可行、还能往更大试;若 m×m 超过 x,这个 m 太大、连同更大的都不行。一刀砍半,把逐个试从 O(√x) 降到 O(log x)。
准备 · 区间 [0,8]:把所有候选答案 0 到 8 排成一排,这排数全程不变。左指针 l 指 0、右指针 r 指 8,圈出搜索范围。答案 ans 先存 0。
第 1 轮 · 取中点 mid=4:取中间位置,mid 等于 0 加 8 除以 2,得 4。下一步用它的平方和 x 比大小。
第 1 轮 · 判断 4×4 > 8:4 乘 4 等于 16,大于 8。说明 4 以及比 4 更大的候选,平方都超了,都不可能是答案。这是负例:往小收。
第 1 轮 · 收右 r = mid-1 = 3:平方太大,把右指针 r 收到 mid 减 1,也就是 3。4 到 8 这半段,连同 mid 自己,灰掉丢弃。ans 仍是 0,没更新。
第 2 轮 · 取中点 mid=1:范围缩到 0 到 3。新的中点,mid 等于 0 加 3 除以 2,向下取整得 1。算 1 的平方。
第 2 轮 · 判断 1×1 ≤ 8 → 记 ans:1 乘 1 等于 1,不超过 8。说明 1 是个可行答案,先把 ans 更新成 1。但右边可能还有更大的可行值,继续往右试。
第 2 轮 · 进左 l = mid+1 = 2:mid 可行,记进 ans 等于 1,再把左指针 l 推到 mid 加 1,也就是 2,去右半 2 到 3 找有没有更大的可行答案。左边 0、1 不必再看。
第 3 轮 · 取中点 mid=2:范围 2 到 3,中点 mid 等于 2 加 3 除以 2,向下取整得 2。算 2 的平方。
第 3 轮 · 判断 2×2 ≤ 8 → 更新 ans:2 乘 2 等于 4,仍不超过 8。又一个更大的可行答案,把 ans 更新成 2,再往右试。
第 3 轮 · 进左 l = mid+1 = 3:ans 更新到 2,左指针 l 推到 3。现在 l 和 r 都指向 3,范围只剩一个候选。
第 4 轮 · 取中点 mid=3:范围只剩 3 到 3,中点就是 3。算最后这个候选 3 的平方。
第 4 轮 · 判断 3×3 > 8 → 收右:3 乘 3 等于 9,大于 8,太大。又一个负例:r 收到 mid 减 1,也就是 2,ans 不更新。这一收,l 是 3 越过了 r 是 2,范围空了。
结束 · 返回 ans=2:收尾:l 越过 r,循环结束。ans 停在最后一次满足平方不超过 x 的值,正是 2,也就是 √8 的整数部分。
灵魂帧慢放 · 命中那一格:把核心动作慢放一遍:每轮只做一件事,拿 mid 的平方和 x 比。不超 x 就把这个 mid 记进 ans 再往大找,超了就往小找。2 是最后一个被记下的可行值,定格在它。
不一定要在数组里二分。只要答案越大越满足、或者越大越不满足,就能直接在答案的取值范围里二分。这一招叫二分答案。
雷区实演 · 返回 l 会差一:亲眼看差一坑:循环退出时,左指针 l 已经冲到 3,但 3 的平方超了 x,不是答案。真正的答案 2 是被 ans 锁住的。所以一定返回 ans,不能返回 l。
这题是二分答案的入门款:答案单调,就在答案范围上砍半。往上走,爱吃香蕉的珂珂、分割数组的最大值都是这一招。去二分专题用同一套骨架连着练,遇到卡点直接问小欧 AI 私教。
参考代码
class Solution: def mySqrt(self, x: int) -> int: l, r = 0, x ans = 0 while l <= r: mid = (l + r) // 2 if mid * mid <= x: ans = mid l = mid + 1 else: r = mid - 1 return ans复杂度
- 时间复杂度:O(log x),每轮把候选范围砍一半,砍到只剩一个最多 log x 刀
- 空间复杂度:O(1),只用 l、r、mid、ans 这几个变量,不随 x 增长
可套用的代码模板
把本题抽成骨架:定义一个 check 判断 mid 可不可行,可行就记 ans 并往大走、不可行往小走。本题的 check 就是 mid 的平方不超过 x。若改求最小可行,把记录和移动方向反过来即可。
# 找「最大的满足 check 的值」l, r, ans = 最小, 最大, 默认值while l <= r: mid = l + (r - l) // 2 if check(mid): # 本题 check 是 mid*mid <= x ans = mid # 记录可行解 l = mid + 1 # 还想要更大,往右 else: r = mid - 1 # 不可行,往左return ans易错点
面试追问把动画讲成自己的话
追问不用二分还能怎么做?
追问为什么 mid 写成 l 加 r 减 l 再除 2,而不是 l 加 r 除 2?
追问如果要保留小数怎么办?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。再去主线里挑下一道练手。
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题