30 秒识别Q1 · 1011 Capacity To Ship Packages Within D Days:传送带每天最多装 cap 重量,货物必须按顺序装运,求 D 天内运完的最小 cap。你的第一反应是哪个模式?
看答案
答案二分(模板 B)。「最小的最大装载量」+「能否在 D 天内完成」——判定函数直接写在题面里了。区间是 [max(weights), sum(weights)],和 410 是同一道题。
所以写二分前先把问题翻译成一个布尔判定函数 ok(x),再确认它单调:一旦某个 x 成立,比它大的全都成立。翻译不出这个函数,就说明这题不是二分。
想通这一层,「答案二分」就不再是新技巧:数组二分的 ok(i) 是 nums[i] >= target,答案二分的 ok(k) 是「速度 k 够不够快」——判定函数换了,骨架一模一样。
| 题面出现… | 想到 |
|---|---|
| 「已排序数组里找目标 / 插入位置 / 第一个大于 X 的」 | 边界二分(模板 A) |
| 「旋转有序数组」 | 二分 + 先判断哪半是有序的 |
| 「最小的最大值」「最大的最小值」「最少需要多少天 / 多快」 | 答案二分(模板 B) |
| 「能否在 X 之内完成」这种问句本身 | 它就是判定函数 ok(X) |
| 数据规模到 10⁹ 但答案是个整数 | 在答案区间上二分,log 级别的判定次数 |
| 「两个有序数组的中位数 / 第 K 小」 | 二分划分位置(认脸级) |
有序数组里找一对数是双指针(第 02 章),找一个位置才是二分。「第 K 大」如果数组无序、且没有可二分的判定量,那是堆或快速选择(第 08 章);但「第 K 小的距离 / 第 K 小的乘积」这类答案本身是数值的题,反而回到答案二分;而「BST 里的第 K 小」根本不用二分,中序遍历走到第 K 个就是(第 07 章)。顺带把「有序」这个词的三种含义分清:有序数组找位置 → 本章;有序链表合并 → 哨兵 + 双指针(第 06 章);BST 的中序天然有序 → 第 07 章。判断标准始终是同一个:能不能写出单调的 ok(x)。
■ 【骨架】 不变,闭着眼默写 ■ 【插槽】 随题变化,写题前先问自己填什么
def lower_bound(nums, target):
lo, hi = 0, len(nums) # 【骨架】左闭右开,hi 取"越界位置"
while lo < hi: # 【骨架】区间空了就停
mid = (lo + hi) // 2 # 【骨架】
if nums[mid] < target: # 【插槽】判定:成立说明 mid 太靠左
lo = mid + 1 # 【骨架】mid 已被排除
else:
hi = mid # 【骨架】mid 可能就是答案,不能排除
return lo # 【插槽】答案:第一个满足条件的下标
lo = mid + 1 / hi = mid、返回 lo。只要判定函数单调,这个骨架不会死循环,因为每轮区间必然缩小。def min_feasible(lo, hi, ok):
while lo < hi: # 【骨架】二分的是"答案区间",不是数组
mid = (lo + hi) // 2 # 【骨架】
if ok(mid): # 【插槽】判定函数:mid 这个答案够用吗
hi = mid # 【骨架】够用,试试更小的
else:
lo = mid + 1 # 【骨架】不够用,答案必须更大
return lo # 【插槽】最小的可行答案
ok 写成独立函数,面试时先讲 ok 再讲二分——面试官要听的是你为什么确信它单调。lo <= hi 那一套,全书统一一个边界约定,考场上少一个出错来源。divmod(mid, n)。识别信号是「每行有序且下一行首元素大于上一行末元素」。nums[mid] 和右端比」。为什么和右端比而不是左端:旋转之后右端点一定落在「较小的那一段」里,比较结果才单调;没有旋转时右端就是最大值,条件恒不成立,hi 一路收到 0,答案照样对。ok(k) = 以速度 k 吃完所需小时数 <= h,区间 [1, max(piles)]。向上取整用 -(-p // k),别用浮点。ok(cap) = 按 cap 贪心切分后段数 <= m,区间 [max(nums), sum(nums)]。认出「最小化最大值」这五个字即可。hi = len(nums)(右开)配 hi = mid;hi = len(nums) - 1(右闭)配 hi = mid - 1。混着写是死循环和差一错误的头号来源。全书统一用左闭右开。ok 的方向反了——求最小可行时 ok 成立要 hi = mid;求最大可行时要把判定取反或改成 lo = mid(此时 mid 必须上取整,否则死循环)。拿不准就一律转化成「求最小」。[1,1,1,0,1] 这种输入下 nums[mid] == nums[hi] 无法判断哪半有序,只能 hi -= 1 退化到 O(n)。面试被追问时要说得出这个边界。30 秒识别Q1 · 1011 Capacity To Ship Packages Within D Days:传送带每天最多装 cap 重量,货物必须按顺序装运,求 D 天内运完的最小 cap。你的第一反应是哪个模式?
答案二分(模板 B)。「最小的最大装载量」+「能否在 D 天内完成」——判定函数直接写在题面里了。区间是 [max(weights), sum(weights)],和 410 是同一道题。
30 秒识别Q2 · 162 Find Peak Element:数组中相邻元素不相等,返回任意一个「比左右邻居都大」的位置。你的第一反应是哪个模式?
二分。数组没有排序也能二分:nums[mid] < nums[mid+1] 说明右边一定存在峰值,这个判定是单调的。它是「有序才能二分」这个误解的最好解药。
30 秒识别Q3 · 在一个无序数组里找第 K 大的元素。你的第一反应是哪个模式?
堆 / 快速选择,不是本章——数组无序,也写不出关于下标的单调判定函数。养一个大小为 K 的小顶堆即可,见第 08 章。对照 162:能不能二分,看的是有没有单调判定,不是看数组长什么样。
每章自测都会混入恰好一道非本章题,防止「读哪章选哪章」的条件反射;题面也一律取自本章未收录的题,避免凭记忆而非凭信号作答。