算法面试模式闪卡:双指针、滑动窗口、图与动态规划

用中文闪卡训练算法面试中的模式识别:从题目信号判断双指针、滑动窗口、图、动态规划等方法,并复习不变量、复杂度、边界条件与常见误选。

Sobre este mazo

这套 216 张中文闪卡面向准备算法面试的候选人,重点是先识别模式,再开始写代码。

卡片练习五类检索:从题目信号选择候选模式;从模式说出适用前提和核心不变量;根据输入与实现判断时间、辅助空间复杂度;从边界条件或失败症状定位漏洞;在易混场景中解释为什么某个模式不合适,并给出更合适的方向。只有当反向检索确实能帮助选型时才单独成卡;“复杂度 → 算法”“答案 → 题名”等歧义大、容易靠猜的映射被排除,也不机械生成所有事实排列。

学习顺序从数组、字符串、哈希表、栈和队列的基础判断开始,再交错引入双指针、滑动窗口、二分查找、链表、区间、树与堆;随后学习图表示、遍历、最短路、拓扑排序和并查集;最后处理回溯、贪心与动态规划。每个主题先建立前提和状态,再进入不变量、复杂度、边界条件与常见误选;相关变体会被错开,避免前一张卡直接提示下一张。

内容覆盖数组与字符串、哈希表、双指针、滑动窗口、栈与队列、二分查找、链表、区间、树、堆、图、并查集、回溯、贪心和动态规划。它不是按命名题目组织的题解卡包,也不是逐题代码合集;不包含完整题面、逐行代码、按公司整理的题库、机械反向卡或特定语言语法记忆。卡片本身不使用图片、音频或其他媒体,唯一媒体是一张原创生成的抽象封面。

所有问题、回答、顺序和元数据均独立创作,封面也是独立生成的原创构图。未复制任何第三方题面、答案、课程文字、代码、示例或图表。算法事实属于通用知识;在权利适用范围内,原创问题、回答、编排、元数据和封面以 CC0 1.0 提供。外部核对资料仍受其各自条款约束。

Tarjetas de este mazo

  1. Tarjeta 1

    Pregunta

    按索引遍历长度为 n 的序列时,最基本的合法边界是什么?

    Respuesta

    合法索引满足 0 <= i < n 空序列的 n = 0,因此一次也不应访问;需要查看 i + 1 时,还要把条件收紧到 i + 1 < n

  2. Tarjeta 2

    Pregunta

    只需反复判断某个值是否出现过,通常先考虑什么结构?

    Respuesta

    先考虑 hash set。 它直接表达 membership;处理 n 个元素时通常用 O(n) 辅助空间,单次查询期望 O(1)。如果还要次数或位置,再改用 hash map。

  3. Tarjeta 3

    Pregunta

    什么处理顺序是使用 stack 的强信号?

    Respuesta

    最新进入、最先处理的 LIFO 顺序。 当一个对象要等到后面的信息出现才结算,而且最近未结算对象必须先处理时,stack 往往最自然。

  4. Tarjeta 4

    Pregunta

    决定原地修改数组前,必须先确认哪项语义前提?

    Respuesta

    必须确认允许改写输入,而且后续不再需要原始排列。 原地算法省辅助空间,但会破坏旧值或顺序;若调用方仍依赖原数据,就应复制或选用非破坏式方案。

  5. Tarjeta 5

    Pregunta

    题目需要持续知道每个值出现了多少次,应维护什么状态?

    Respuesta

    维护 value -> count 的 hash map。 读到一个值就更新其计数;这样 membership 只是 count > 0 的特例,也能正确区分重复值。

  6. Tarjeta 6

    Pregunta

    需要按发现顺序处理待办对象时,通常用什么结构保存 frontier?

    Respuesta

    用 FIFO queue。 先发现的对象先出队,新的对象从队尾加入,因此处理顺序稳定;如果改用 stack,顺序会变成深度优先式的后进先出。

  7. Tarjeta 7

    Pregunta

    许多查询都要重复使用某位置左侧或右侧的聚合结果时,先考虑什么预处理?

    Respuesta

    先考虑 prefix 或 suffix 聚合。 一次 O(n) 预处理后,读取某个已保存的前缀或后缀结果是 O(1);若查询任意中间区间,还要确认聚合能否用逆运算拆分,或改用别的结构。

  8. Tarjeta 8

    Pregunta

    既要按值快速查找,又要返回原始位置时,hash map 应保存什么?

    Respuesta

    保存 value -> index,或在重复值有意义时保存 value -> indices 不能默认一个值只有一个位置;覆盖旧索引是否正确,取决于需要任意位置、最近位置还是全部位置。

  9. Tarjeta 9

    Pregunta

    检查成对分隔符的嵌套是否合法时,stack 应保存什么?

    Respuesta

    保存尚未匹配的 opening delimiter。 遇到 closing delimiter 时,它必须匹配栈顶;栈空却要关闭,或扫描结束后栈仍非空,都表示结构不合法。

  10. Tarjeta 10

    Pregunta

    什么时候不能为了简化扫描而直接排序输入?

    Respuesta

    当原始顺序或原始索引属于答案语义时,不能直接排序。 Comparison sort 通常花 O(n log n) 时间并重排元素;若排序仍有价值,可把原索引与值一起保存,或改用不破坏顺序的状态结构。

  11. Tarjeta 11

    Pregunta

    既要去重又要保留首次出现顺序时,为什么不能只返回 hash set?

    Respuesta

    因为普通 hash set 只表达唯一性,不保证还原首次出现顺序。 扫描原序列时用 set 判断是否见过,并把首次出现的值追加到结果,才能同时满足两项要求。

  12. Tarjeta 12

    Pregunta

    算法需要在两端都进行 O(1) 级别的插入或删除时,应优先考虑什么结构?

    Respuesta

    优先考虑 deque。 queue 只暴露固定的入队端和出队端,stack 只操作一端;deque 明确支持两端操作。这里的 O(1) 指常见 deque 实现的摊还或最坏界,需以具体实现为准。

  13. Tarjeta 13

    Pregunta

    判断数组扫描复杂度时,比“代码有几层循环”更可靠的做法是什么?

    Respuesta

    计算每个元素被访问的总次数。 若长度为 n 的序列中,每个元素只被常数次访问,且每次处理为 O(1),总时间就是 O(n);若对每个位置都重新扫描一个随 n 增长的范围,才可能达到 O(n²)

  14. Tarjeta 14

    Pregunta

    如何准确表述 hash table 查找、插入和删除的复杂度?

    Respuesta

    通常是期望或摊还 O(1),最坏 O(n) 结论依赖分布良好的 hash、受控装载因子和扩容策略;严重碰撞可让一次操作退化为线性扫描。保存最多 n 个键时,辅助空间为 O(n)

  15. Tarjeta 15

    Pregunta

    哪类“最近一个更大或更小元素”查询常提示 monotonic stack?

    Respuesta

    当元素按顺序到达,并要找最近的支配元素时,考虑 monotonic stack。 栈中只保留仍可能成为答案的候选;新元素会淘汰已不可能再被选中的栈顶。

  16. Tarjeta 16

    Pregunta

    什么输入特征会让相向双指针成为候选模式?

    Respuesta

    有序序列加上可单调判断的关系,是强信号。 当当前结果偏小只可能通过移动 left 改善、偏大只可能通过移动 right 改善时,每一步才能安全排除一批候选。

  17. Tarjeta 17

    Pregunta

    根节点和 leftright 引用如何表示一棵二叉树?

    Respuesta

    从根节点沿 leftright 引用可到达全部节点。每个引用表示一条父到子的边;树结构还要求无环,并且除根外每个节点恰有一个父节点。

  18. Tarjeta 18

    Pregunta

    要比较所有长度恰好为 k 的连续片段时,应怎样避免每次重算?

    Respuesta

    维护固定长度 sliding window。 窗口右移一格时加入新元素、移除离开的元素,便可复用上一窗口的 state;若更新各为 O(1),扫描长度 n 的序列总计 O(n)

  19. Tarjeta 19

    Pregunta

    递归 DFS 遍历树时,每次调用最自然的子问题是什么?

    Respuesta

    以当前节点为根的整棵子树。调用处理当前节点,并从左右子树拿回结果;这让递归参数和返回值都围绕一个明确的子树定义。

  20. Tarjeta 20

    Pregunta

    使用半开区间写 binary search 时,循环不变量应怎样表述?

    Respuesta

    候选区间始终是 [left, right) 初始化通常为 left = 0, right = n,循环条件是 left < right;每次更新必须严格缩短这个区间,空输入自然得到 [0, 0)

  21. Tarjeta 21

    Pregunta

    树的 BFS 怎样准确分隔当前层与下一层?

    Respuesta

    处理一层前先记录当前队列长度 levelSize,只弹出这 levelSize 个节点。处理期间新入队的孩子自然属于下一层。

  22. Tarjeta 22

    Pregunta

    链表操作会频繁改动头节点时,sentinel 能消除哪类分支?

    Respuesta

    它把“头节点前面没有 predecessor”这个特例变成普通情况。sentinel.next 指向真实头节点后,删除、插入和合并都可统一通过前驱重连,最后返回 sentinel.next

  23. Tarjeta 23

    Pregunta

    需要先处理父节点,再处理它的子树时,应选哪种遍历顺序?

    Respuesta

    前序遍历:先当前节点,再左子树,最后右子树。它适合把父节点状态传给后代,或在进入子树前完成处理。

  24. Tarjeta 24

    Pregunta

    处理区间前,为什么必须先确定闭区间还是半开区间?

    Respuesta

    因为端点相等时是否重叠取决于区间约定。 闭区间 [a, b][b, c] 共享 b;半开区间 [a, b)[b, c) 不重叠。排序、合并和事件 tie-break 都必须沿用同一约定。

  25. Tarjeta 25

    Pregunta

    m 个元素的 binary heap 中,读取 top、pushpop 各是什么复杂度?

    Respuesta

    读取 top 是 O(1)push 和弹出 top 都是 O(log m)。后两者最多沿堆高调整一次,binary heap 的高度是 O(log m)

  26. Tarjeta 26

    Pregunta

    相向双指针每次移动一端时,必须证明什么?

    Respuesta

    必须证明被越过的候选不可能优于或满足答案。 这个 discard invariant 通常来自排序与单调关系;若无法证明,移动指针只是猜测,可能漏掉有效组合。

  27. Tarjeta 27

    Pregunta

    当前节点的答案依赖左右子树结果时,应优先想到哪种遍历顺序?

    Respuesta

    后序遍历:先求左右子树,再处理当前节点。高度、子树大小和子树聚合都符合“孩子先完成,父节点再合并”的结构。

  28. Tarjeta 28

    Pregunta

    可变 sliding window 能靠移动 left 修复违规状态,需要什么前提?

    Respuesta

    可行性必须对窗口收缩呈单调变化。 固定 right 后,移除左端元素应当只朝“更可行”的方向走;若收缩可能反复让状态变好又变坏,就不能用单向 left 安全排除候选。

  29. Tarjeta 29

    Pregunta

    为什么把 m 个已有元素自底向上建成 binary heap 是 O(m),而不是 O(m log m)

    Respuesta

    因为自底向上的 heapify 只让少量靠近根的节点下沉较远,大多数节点移动很短或不移动;按高度汇总工作量是 O(m)。逐个 push 才是 O(m log m)

  30. Tarjeta 30

    Pregunta

    怎样把“找第一个满足条件的位置”写成稳定的 binary search 目标?

    Respuesta

    P(i) 从 false 单调变为 true 的前提下,搜索最小的 true 位置。P(mid) 为真时保留 mid 并收紧右界,否则排除 mid 及其左侧;结束后还要检查返回位置是否在范围内且确实满足条件。

  31. Tarjeta 31

    Pregunta

    二叉树的中序遍历在什么前提下会得到非降序序列?

    Respuesta

    这棵树必须满足 BST 的全局顺序约束,并采用一致的重复值策略。中序按左子树、当前节点、右子树访问;普通二叉树没有有序保证。

  32. Tarjeta 32

    Pregunta

    迭代反转单链表时,prevcurrent 应保持什么不变量?

    Respuesta

    prev 始终是已反转前缀的头,current 是尚未处理后缀的头。 改写 current.next 前先保存原来的 next,再依次推进 prevcurrent,才不会丢失后缀。

  33. Tarjeta 33

    Pregunta

    递归处理空子树时,base case 应返回什么?

    Respuesta

    返回当前子问题的中性结果,而不是随手写一个固定值。例如计数返回 0,高度按所选定义返回 0-1,布尔验证常返回 true。定义必须与合并公式一致。

  34. Tarjeta 34

    Pregunta

    两个半开区间 [a, b)[c, d) 的重叠条件是什么?

    Respuesta

    重叠当且仅当 max(a, c) < min(b, d) 严格小于体现了半开语义:b = c 只是首尾相接,不共享任何点。若改成闭区间,相应比较通常要允许端点相等。

  35. Tarjeta 35

    Pregunta

    扫描 n 个元素并保留最大的 k 个时,小根堆应维持什么不变量?

    Respuesta

    堆中始终是当前见过元素里最大的至多 k 个,堆顶是候选里的最小门槛。假设 1 ≤ k ≤ n;堆未满时直接加入,满后只用更大值替换 top。时间 O(n log k),辅助空间 O(k)

  36. Tarjeta 36

    Pregunta

    原地筛选或压缩数组时,read/write pointers 的核心不变量是什么?

    Respuesta

    [0, write) 始终是已经确定的输出前缀,read 扫描尚未处理的输入。 每遇到应保留的元素,就写到 write 并递增;前提是允许覆盖已读区域。

  37. Tarjeta 37

    Pregunta

    访问一棵含 n 个节点、高度为 h 的树一次,递归遍历的时间和辅助空间是多少?

    Respuesta

    时间通常是 O(n),递归辅助空间是 O(h)。每个节点只处理常数次;调用栈只保存当前根到节点的路径。输出本身占用的空间应另计。

  38. Tarjeta 38

    Pregunta

    sliding window 的计数或总和状态必须始终对应什么范围?

    Respuesta

    必须精确对应当前窗口 [left, right][left, right),二者选定一种。 扩张时先加入新元素,收缩时移除离开元素;边界约定和更新顺序不一致会产生 off-by-one。

  39. Tarjeta 39

    Pregunta

    数据逐个到达、总长度未知,但只需随时保留最大的 k 个值,应选什么结构?

    Respuesta

    选容量为 k 的小根堆,并假设 k ≥ 1。它不需要保存完整数据流;每个输入最坏做一次 O(log k) 的堆更新,辅助空间保持 O(k)

  40. Tarjeta 40

    Pregunta

    “最后一个满足条件的位置”怎样借助边界 binary search 表达?

    Respuesta

    在 predicate 从 true 单调变为 false 时,先找第一个 false,再取其前一个位置。 这把右边界问题转成统一的左边界模板;返回前必须检查前一个位置存在,并确实属于有效范围。

  41. Tarjeta 41

    Pregunta

    用栈实现“根、左、右”的迭代 DFS 时,孩子应按什么顺序入栈?

    Respuesta

    先压入右孩子,再压入左孩子。栈是 LIFO,左孩子因此先弹出;若反过来入栈,实际访问顺序会变成“根、右、左”。

  42. Tarjeta 42

    Pregunta

    为什么“单链表删除是 O(1)”必须附带节点定位前提?

    Respuesta

    只有待改写的 predecessor 已知时,局部重连才是 O(1) 若先要从头找到第 k 个节点、目标值或其前驱,定位本身是 O(n);不能把查找成本藏在删除操作之外。

  43. Tarjeta 43

    Pregunta

    合并 k 个各自非降序、总计 N 个元素的序列时,堆里应保存什么?

    Respuesta

    保存每个尚未耗尽序列的当前头部候选。每次弹出全局最小值,再把同一序列的下一个值入堆;时间 O(N log k),辅助空间 O(k)

  44. Tarjeta 44

    Pregunta

    按起点排序后合并区间时,应保持什么扫描不变量?

    Respuesta

    结果中已完成区间互不重叠,最后一个区间代表当前可扩展的 union。 新区间若按既定端点语义与它重叠,就更新终点;否则追加为下一段。按起点排序保证后续起点不会倒退。

  45. Tarjeta 45

    Pregunta

    树的 BFS 为什么不能笼统地说只占 O(h) 辅助空间?

    Respuesta

    因为队列保存的是一段横向 frontier,空间由最大层宽 w 决定,即 O(w),最坏可达 O(n)h 描述的是高度,更直接对应 DFS 路径栈。

  46. Tarjeta 46

    Pregunta

    判断一个序列能否按顺序匹配到另一个序列中时,双指针如何分工?

    Respuesta

    一个指针指向下一个待匹配元素,另一个只向前扫描候选序列。 匹配成功才推进前者;不变量是前者之前的元素已按顺序匹配,扫描过的位置无需回看。

  47. Tarjeta 47

    Pregunta

    只保留 k 个候选时,第 k 大与第 k 小分别该用哪一侧的堆?

    Respuesta

    假设 1 ≤ k ≤ n:第 k 大用容量 k 的小根堆;第 k 小用容量 k 的大根堆。堆顶始终是当前候选集合里最容易被淘汰的边界值。

  48. Tarjeta 48

    Pregunta

    求最长可行窗口与最短可行窗口时,收缩时机有什么区别?

    Respuesta

    最长问题通常在窗口违规时收缩;最短问题通常在窗口仍可行时持续收缩并更新答案。 两者都依赖单调可行性,但优化目标决定何时记录候选。

  49. Tarjeta 49

    Pregunta

    n 个节点的树做递归 DFS 时,调用栈何时是 O(log n),何时会退化为 O(n)

    Respuesta

    只有树高 h = O(log n),例如树明确平衡时,调用栈才是 O(log n);链状或严重偏斜的树有 h = O(n),调用栈也会达到 O(n)

  50. Tarjeta 50

    Pregunta

    没有显式有序数组时,什么条件仍允许在 answer space 上二分?

    Respuesta

    候选答案必须有序,而且可行性 predicate 在某个边界只改变一次真假。 每次检查 mid 后,predicate 要能证明一整侧都可排除;检查本身不必是 O(1)

  51. Tarjeta 51

    Pregunta

    什么时候直接排序通常比维护 top-k 堆更合适?

    Respuesta

    需要完整有序结果,或 kn 同量级时,排序通常更简单。comparison sort 用 O(n log n) 时间;容量 k 的堆适合只取少量候选,用 O(n log k) 时间和 O(k) 辅助空间。

  52. Tarjeta 52

    Pregunta

    要在一次遍历中找到单链表中点,fast/slow pointers 应怎样移动?

    Respuesta

    slow 每次走一步,fast 每次走两步。fast 到达末尾时,slow 约走了链长的一半;偶数长度时返回两个中点中的哪一个,取决于循环条件,必须事先约定。

  53. Tarjeta 53

    Pregunta

    沿一个孩子方向查找 BST 的做法依赖什么前提,复杂度怎样写?

    Respuesta

    前提是整棵树满足约定的 BST 顺序。查找时间是 O(h);只有明确平衡时才是 O(log n),任意 BST 在偏斜时最坏为 O(n)

  54. Tarjeta 54

    Pregunta

    要计算任一时刻同时活跃的区间数,扫描线维护什么量?

    Respuesta

    维护当前 active count,并记录其最大值。 按时间处理开始与结束事件:开始时加一,结束时减一;端点相同时谁先处理,必须由闭区间或半开区间语义决定。

  55. Tarjeta 55

    Pregunta

    为什么 binary heap 不适合查找一个任意给定值?

    Respuesta

    堆只保证父子之间的偏序,不保证左右子树内部有序。除 top 外,一个值可能出现在许多位置,因此查找任意值最坏仍要扫描 O(m) 个元素。

  56. Tarjeta 56

    Pregunta

    双指针代码含嵌套 while 时,何时总时间仍是 O(n)

    Respuesta

    当每个指针都只单向移动,且各自总共最多跨过 n 个位置时。 内层循环的所有迭代可以按指针移动次数合计,因此是 O(n + n) = O(n),不是逐次相乘。

  57. Tarjeta 57

    Pregunta

    在 BST 中只关心闭区间 [L, R] 内的值时,何时可剪掉整侧子树?

    Respuesta

    当前值小于 L 时可跳过左子树;当前值大于 R 时可跳过右子树。这个剪枝依赖有效 BST 的全局顺序和已明确的闭区间语义。

  58. Tarjeta 58

    Pregunta

    窗口允许重复值且要精确移除左端元素时,为什么 set 往往不够?

    Respuesta

    因为 set 不记录同一值在窗口中还有几份。 应维护 frequency map;元素离开时减一,只有计数降到零才删除键,这样窗口 state 才不会过早宣告该值消失。

  59. Tarjeta 59

    Pregunta

    堆中旧条目无法高效定位删除时,lazy deletion 怎样保持 top 正确?

    Respuesta

    另存最新状态或有效标记;每次读取 top 前,持续弹出已失效条目。每次弹出是 O(log m),但每个旧条目最多清理一次,所以总清理成本可摊还;堆仍可能暂时保存所有未清理条目。

  60. Tarjeta 60

    Pregunta

    如果 predicate 随候选值反复在 true 与 false 之间切换,为什么不能 binary search?

    Respuesta

    因为一次判断无法安全排除完整的一侧。 Binary search 需要真假区间只有一个分界;predicate 非单调时,mid 的结果不能说明左侧或右侧全部无效。

  61. Tarjeta 61

    Pregunta

    为什么只比较 BST 节点与它的直接孩子不足以验证整棵树?

    Respuesta

    因为更深的后代也必须满足所有祖先带来的范围限制。递归时应传递允许的下界和上界,并按既定重复值策略决定边界是严格还是可包含。

  62. Tarjeta 62

    Pregunta

    为什么一快一慢两个指针能用 O(1) 辅助空间检测链表中的环?

    Respuesta

    进入环后,fast 每轮相对 slow 多前进一步,因此最终会追上它。fast 先到达 null,链表无环;整个过程最多线性步数,所以时间 O(n)、辅助空间 O(1)

  63. Tarjeta 63

    Pregunta

    用双堆维护数据流中位数时,需要维持哪两个不变量?

    Respuesta

    小值半区用大根堆、大值半区用小根堆;两边大小最多相差 1,且前者所有值不大于后者所有值。插入是 O(log n),读中位数是 O(1),空间是 O(n)

  64. Tarjeta 64

    Pregunta

    半开区间 [start, end) 的扫描线中,同一时刻的结束和开始事件谁先处理?

    Respuesta

    先处理结束,再处理开始。 因为在半开语义下,旧区间在 end 已不活跃,新区间可从同一时刻开始;反过来会把本不重叠的两段短暂计为同时活跃。

  65. Tarjeta 65

    Pregunta

    BST 允许重复值时,为什么必须先约定重复值放在哪一侧?

    Respuesta

    因为搜索、插入、验证和中序有序性的边界都依赖同一约定。例如“左侧严格小于、右侧大于等于”与“两侧都严格”不是同一个数据结构契约。

  66. Tarjeta 66

    Pregunta

    无序序列上的关系不随指针移动单调变化时,为什么不应硬套相向双指针?

    Respuesta

    因为移动任一端都无法证明被跳过的组合无效。 若排序不破坏答案语义,可以先排序建立单调性;否则应考虑 hash 状态、完整搜索或别的能保留候选的方法。

  67. Tarjeta 67

    Pregunta

    每个节点都要汇总其整棵子树的信息时,递归函数应返回什么?

    Respuesta

    返回父节点合并时真正需要的子树摘要,例如大小、高度或最佳向下值。这样每个节点只合并一次,通常是 O(n) 时间和 O(h) 调用栈。

  68. Tarjeta 68

    Pregunta

    窗口收缩到空时,哪些 state 必须同步恢复?

    Respuesta

    所有只描述窗口内容的 state 都必须回到空状态。 计数、总和、distinct 数和 deque 都不能残留已移除元素;同时边界应满足所选约定,例如半开窗口 [left, right)left = right 时为空。

  69. Tarjeta 69

    Pregunta

    DFS 共用一个可变 path 记录当前根到节点路径时,离开节点前必须做什么?

    Respuesta

    必须撤销本层加入的节点,也就是 pop。不回退会让一个分支的节点泄漏到兄弟分支;进入时 push、退出时 pop 正好维持“path 等于当前递归路径”的不变量。

  70. Tarjeta 70

    Pregunta

    长度为 n 的候选区间每轮至少减半时,迭代和递归 binary search 的复杂度分别是什么?

    Respuesta

    两者时间都是 O(log n);迭代辅助空间 O(1),递归调用栈 O(log n) 前提是每轮检查与边界更新为 O(1);若每次 predicate 检查的上界为 C(n),总时间上界是 O(C(n) log n)

  71. Tarjeta 71

    Pregunta

    树中经过当前节点的最长简单路径,为什么要组合两个孩子方向的贡献?

    Respuesta

    因为这条路径可从一个子树上来,再进入另一个子树,所以要组合两个最大的向下长度;但返回给父节点时只能选其中一个方向。节点数或边数的口径要从 base case 到公式保持一致。

  72. Tarjeta 72

    Pregunta

    快慢指针在环内相遇后,怎样找到环的入口?

    Respuesta

    把一个指针移回头节点,再让两个指针每次各走一步;下一次相遇点就是环入口。 这个结论来自相遇时走过距离的整圈关系,不需要额外 set,整体时间 O(n)、辅助空间 O(1)

  73. Tarjeta 73

    Pregunta

    树 DFS 把答案放在跨调用复用的全局变量里,最常见的正确性风险是什么?

    Respuesta

    旧结果会污染新的遍历,兄弟分支也可能意外共享不该共享的状态。优先让递归返回子树结果;确需共享聚合量时,把它限制在单次调用范围并明确初始化。

  74. Tarjeta 74

    Pregunta

    先按端点排序、再线性扫描 n 个区间时,整体复杂度应怎样写?

    Respuesta

    时间是 O(n log n),由 comparison sort 主导;扫描是 O(n) 辅助空间取决于排序实现,不能一概写成 O(1);若把返回结果也计入总空间,最多 n 个新区间另占 O(n)

  75. Tarjeta 75

    Pregunta

    怎样在一次后序遍历中同时计算树高并发现不平衡子树?

    Respuesta

    让递归返回高度或一个“不平衡” sentinel。任一孩子已不平衡,或左右高度差超过允许值,就继续向上传 sentinel;否则返回当前高度。这样是 O(n),避免反复计算高度导致最坏 O(n²)

  76. Tarjeta 76

    Pregunta

    monotonic stack 为什么常能在线性时间内完成整次扫描?

    Respuesta

    每个元素最多入栈一次、出栈一次。 虽然某一步可能连续弹出很多元素,但对 n 个元素合计只有 O(n) 次 push/pop;栈最多保存 n 个候选,辅助空间 O(n)

  77. Tarjeta 77

    Pregunta

    看到“树上的路径”时,写算法前最先要澄清什么?

    Respuesta

    先澄清端点范围:根到叶、根到任意节点,还是任意节点到任意节点。还要确认长度按边还是按节点计;这些定义会直接改变 base case 和合并方式。

  78. Tarjeta 78

    Pregunta

    可变 sliding window 有内层收缩循环时,何时总时间是 O(n)

    Respuesta

    rightleft 都只向前,且各自最多移动 n 次时。 对长度 n 的序列,所有扩张与收缩合计 O(n);辅助空间等于窗口 state,可从 O(1) 到最多 O(n)

  79. Tarjeta 79

    Pregunta

    即使递归 DFS 的渐进空间是正确的,什么树形仍可能让实现栈溢出?

    Respuesta

    高度接近 n 的链状或严重偏斜树。它需要 O(n) 层调用;输入深度可能超过运行时栈限制时,应改用显式栈或确认环境能承受该深度。

  80. Tarjeta 80

    Pregunta

    整数 binary search 中,怎样计算 mid 更不容易溢出?

    Respuesta

    使用 mid = left + (right - left) / 2 的整数取整形式。 它避免先计算可能溢出的 left + right;向下还是向上取整要与边界更新配套,确保候选区间每轮严格缩小。

  81. Tarjeta 81

    Pregunta

    普通根树中,后序 DFS 如何识别两个不同且已知存在节点的最低共同祖先?

    Respuesta

    找最深的汇合点:当前节点自身和各子树返回的命中信号中,只要有两路覆盖两个目标,当前节点就是最低共同祖先。若目标不保证都存在,还必须另行确认两者确实被找到。

  82. Tarjeta 82

    Pregunta

    合并两个已按同一规则排序的链表时,tail 应保持什么不变量?

    Respuesta

    tail 始终指向已合并有序前缀的最后节点。 每次比较两个当前节点,把较小者接到 tail.next 并推进对应指针;一条链耗尽后,剩余有序后缀可以整体接上。

  83. Tarjeta 83

    Pregunta

    二叉树里什么才算叶节点?空树和单节点树分别怎样判断?

    Respuesta

    叶节点是自身存在且 leftright 都为空的节点。空树没有叶节点;单节点树的根同时也是叶节点。不能把空引用本身当成叶节点。

  84. Tarjeta 84

    Pregunta

    用两个 stack 实现 queue 时,怎样得到摊还 O(1) 的出队?

    Respuesta

    入队压入 in;只有 out 为空时,才把 in 全部倒入 out 每个元素最多被压入和弹出常数次,所以一串 m 次操作总计 O(m);单次搬运仍可能是 O(n)

  85. Tarjeta 85

    Pregunta

    树题中,什么信号更偏向 BFS,什么信号更偏向 DFS?

    Respuesta

    逐层、离根最近或最少边数更偏向 BFS;子树聚合、根到节点状态和回溯路径更偏向 DFS。两者通常都能遍历全树,选择依据是需要的访问顺序与状态形状。

  86. Tarjeta 86

    Pregunta

    V 个顶点、E 条边的稀疏图,通常应选哪种邻接表示?

    Respuesta

    通常选 adjacency list。它占 O(V+E) 空间,并能只遍历真实存在的邻边;实现时也要为孤立顶点保留空邻接表或等价记录。

  87. Tarjeta 87

    Pregunta

    需要列举一连串选择形成的所有可行结果时,应先把搜索过程看成什么结构?

    Respuesta

    决策树。每一层表示一次选择,每条边表示一个候选动作,叶子或满足终止条件的节点表示完整结果;这正是回溯适用的基本信号。

  88. Tarjeta 88

    Pregunta

    adjacency list 中,有向边与无向边应怎样记录?

    Respuesta

    有向边 u → v 只把 v 放进 u 的邻接表;无向边 {u, v} 通常同时记录 u → vv → u。若 E 按逻辑无向边计数,存两次仍是 O(V+E) 空间。

  89. Tarjeta 89

    Pregunta

    回溯中 choose → explore → unchoose 的核心不变量是什么?

    Respuesta

    每次递归返回后,共享状态必须恢复到选择前的样子。这样下一个候选分支看到的是同一个父状态,而不是被前一分支污染的状态。

  90. Tarjeta 90

    Pregunta

    图的 BFS 为什么通常要在入队时标记 visited,而不是出队时?

    Respuesta

    入队时标记可保证每个顶点只进入队列一次。若等到出队,同一顶点可能被多个前驱重复入队,放大时间和空间;无权图中,首次发现时的距离也已是最短距离。

  91. Tarjeta 91

    Pregunta

    为什么删除单链表中的当前节点通常需要它的 predecessor?

    Respuesta

    因为要把 predecessor.next 改为 current.next 单链表没有反向链接;若只拿到当前节点,通用删除无法更新前一条边,尾节点尤其不能靠复制后继值处理。

  92. Tarjeta 92

    Pregunta

    什么时候 adjacency matrix 比 adjacency list 更合适?

    Respuesta

    图很稠密,或需要频繁 O(1) 判断任意两点是否有边时,matrix 更合适。代价是 O(V²) 空间,枚举一个顶点的全部邻居也要扫描 O(V) 个位置。

  93. Tarjeta 93

    Pregunta

    怎样判断回溯递归参数是否记录了足够但不过量的状态?

    Respuesta

    状态应恰好决定“接下来还能选什么”和“何时得到结果”。能由现有参数推导的信息不必重复存;缺少会影响合法候选的信息,则会让不同子问题被错误混在一起。

  94. Tarjeta 94

    Pregunta

    图的 DFS 为什么不能像树遍历那样只递归所有邻居?

    Respuesta

    图可能有环,也可能有多条路径到同一顶点,所以必须记录访问状态,避免无限递归和重复处理。仅有一个 visited 集合足够做普通可达性遍历,但某些环检测还需要更细状态。

  95. Tarjeta 95

    Pregunta

    回溯的终止条件需要同时保证哪两件事?

    Respuesta

    保证正确收集完整解,并阻止搜索越过有效深度。条件太早会漏解,太晚会访问无意义或越界状态;应直接对应“一个候选解已经完整”的定义。

  96. Tarjeta 96

    Pregunta

    无权图中,求源点到各点的最少边数应选什么算法?

    Respuesta

    选 BFS。它按距离层扩展,第一次发现顶点时就得到最少边数;用 adjacency list 时,时间 O(V+E),队列和访问状态占 O(V)

  97. Tarjeta 97

    Pregunta

    用 monotonic deque 维护窗口最大值时,deque 中应保留什么?

    Respuesta

    按值单调递减排列、且仍在窗口内的候选索引。 新元素进入时从队尾删掉不大于它的旧候选,窗口左移时从队首删掉过期索引;队首因此始终是当前最大值的位置。

  98. Tarjeta 98

    Pregunta

    用 adjacency list 完整遍历图时,BFS 或 DFS 的复杂度怎样写?

    Respuesta

    时间是 O(V+E),辅助状态通常是 O(V),不含图本身。完整遍历要从每个未访问顶点启动一次;无向边虽在邻接表出现两次,不改变渐进复杂度。

  99. Tarjeta 99

    Pregunta

    回溯里的 path 与控制搜索的状态有什么区别?

    Respuesta

    path 保存当前已选内容;控制状态决定下一步候选,例如位置、剩余额度或已用集合。两者可能重叠,但不能因为 path 可展示结果,就假定它包含全部搜索约束。

  100. Tarjeta 100

    Pregunta

    怎样用一次总体线性的遍历统计无向图的连通分量?

    Respuesta

    依次检查所有顶点;每遇到一个未访问顶点,就从它启动一次 BFS 或 DFS,并把分量数加一。用 adjacency list 时,每个顶点和边总共只处理常数次,时间 O(V+E)

  101. Tarjeta 101

    Pregunta

    设计回溯时,“生成候选”这一步应回答什么问题?

    Respuesta

    它应列出从当前状态出发所有且仅有的合法下一步。漏掉候选会破坏完备性,加入非法候选则需要额外检查并扩大搜索树。

  102. Tarjeta 102

    Pregunta

    同一个图改用 adjacency matrix 后,完整 BFS 或 DFS 为什么通常变成 O(V²)

    Respuesta

    因为每访问一个顶点,都要扫描长度为 V 的整行来找邻居;最多扫描 V 行,所以是 O(V²),即使实际边很少也是如此。matrix 本身也占 O(V²) 空间。

  103. Tarjeta 103

    Pregunta

    改写单链表的 current.next 前,最常见的防丢链动作是什么?

    Respuesta

    先把原来的 current.next 保存到临时变量。 一旦覆盖这条链接,又没有其他引用指向后缀,后续节点就无法继续访问;重连顺序应明确每一步仍能到达未处理部分。

  104. Tarjeta 104

    Pregunta

    用 DFS 检测有向图环时,为什么需要 white/gray/black 三种状态?

    Respuesta

    gray 表示顶点仍在当前递归路径上,遇到指向 gray 的边就说明有环;black 表示该顶点已完整处理。单一 visited 无法区分“当前路径上的祖先”和“其他已完成分支”。

  105. Tarjeta 105

    Pregunta

    把当前 path 加入结果集时,为什么通常要保存副本?

    Respuesta

    因为 path 往往是后续会继续修改的可变容器。若只保存同一个引用,回溯撤销和后续选择会改掉已经收集的结果。

  106. Tarjeta 106

    Pregunta

    无权图 BFS 能保证最短距离的核心队列不变量是什么?

    Respuesta

    队列按从小到大的距离层处理顶点。距离为 d 的顶点只会发现尚未访问、距离为 d+1 的邻居,因此一个顶点首次被发现时,不可能还存在更短的未处理路径。

  107. Tarjeta 107

    Pregunta

    只需生成无顺序的选择组合时,递归参数常用什么来避免反向重复?

    Respuesta

    使用单调前进的 start 索引。下一层只从当前索引之后选择,使同一组元素只按一种顺序生成,而不是再生成顺序相反的副本。

  108. Tarjeta 108

    Pregunta

    简单无向图中,DFS 遇到已访问邻居时,怎样区分父边与环?

    Respuesta

    把进入当前顶点的 parent 一并传入;已访问邻居若不是 parent,就存在环。这个判断假设没有平行边;多重图要追踪边 ID,不能只比较父顶点。

    深蓝色背景上,中央光点连接双指针、滑动窗口、树、图、搜索路径和状态网格的抽象图形

    216 tarjetas

    算法面试模式闪卡:双指针、滑动窗口、图与动态规划

    Estudia este mazo gratis

    Flashcards se abrirá para que puedas empezar a estudiar.

  109. Tarjeta 109

    Pregunta

    monotonic deque 扫描 n 个元素时,为什么总维护成本是 O(n)

    Respuesta

    每个索引最多从队尾加入一次,并从队首或队尾删除一次。 某一步虽可能连续删除多个索引,整次扫描的 deque 操作总数仍为 O(n);最坏辅助空间为 O(n)

  110. Tarjeta 110

    Pregunta

    为什么普通 DFS 不能保证找到无权图中的最短路径?

    Respuesta

    DFS 会先沿一个分支走深,第一次到达目标的路径可能很长。无权最短路应使用按层扩展的 BFS;只有树中两点路径唯一等特殊结构,遍历顺序才不影响那条路径本身。

  111. Tarjeta 111

    Pregunta

    一个剪枝条件要正确,必须证明什么?

    Respuesta

    必须证明被剪掉的前缀不可能扩展成任何所需解。剪枝只是减少访问状态,不能靠“通常没有希望”来牺牲完备性。

  112. Tarjeta 112

    Pregunta

    题目要求所有依赖都先于依赖者出现时,应想到什么图模型与算法?

    Respuesta

    建有向依赖图并求 topological order。只有 DAG 才存在这种顺序;若图中有向环存在,就不可能同时满足所有先后约束。

  113. Tarjeta 113

    Pregunta

    每个位置都可从尚未使用的元素中选择时,状态通常要增加什么?

    Respuesta

    增加 used 集合或布尔数组。它记录当前路径已经占用的元素,使每层都能从全部未用元素中选择;这适合顺序会产生不同结果的排列型状态。

  114. Tarjeta 114

    Pregunta

    无权图中要计算每个顶点到最近源点的距离,多个源点应怎样启动 BFS?

    Respuesta

    把所有源点都以距离 0 同时入队,再做一次 BFS。它们共同形成第 0 层,后续首次发现距离就是到任一源点的最短距离;adjacency list 上仍是 O(V+E)

  115. Tarjeta 115

    Pregunta

    算法频繁按第 k 个位置访问元素时,为什么单链表通常不是合适选择?

    Respuesta

    因为单链表没有随机访问,第 k 个位置需要从头走 O(k) 数组可按索引 O(1) 访问;链表的优势是已知位置附近的重连,不是按下标查找。

  116. Tarjeta 116

    Pregunta

    边不断加入,只需回答“两点现在是否连通”时,应优先想到什么结构?

    Respuesta

    优先想到 union-find。每条新边用 union 合并分量,查询时比较两个点的 find 结果;它适合增量式无向连通,不负责恢复具体路径。

  117. Tarjeta 117

    Pregunta

    找到一个可行解后能否立即返回,取决于什么?

    Respuesta

    取决于目标是“任意一个解”还是“全部解/最优解”。前者可短路;后者仍需探索其他可能分支,除非已有正确的最优性界。

  118. Tarjeta 118

    Pregunta

    带权图的边权满足什么条件时,可以用 Dijkstra 求单源最短路?

    Respuesta

    所有可达边的权重都必须非负。Dijkstra 每次确定当前 tentative distance 最小的未确定顶点;非负权保证以后经过更远顶点不可能把它改得更小。

  119. Tarjeta 119

    Pregunta

    输入含重复值且结果按值去重时,怎样跳过同一层的重复分支?

    Respuesta

    可为每层维护 seen;若排序不改变题意,也可先让相同值相邻,再跳过同层的等值候选。只跳同层重复,跨层出现同值可能代表合法的再次选择。

  120. Tarjeta 120

    Pregunta

    Kahn 拓扑排序中,入度为 0 的队列表示什么?

    Respuesta

    它保存当前所有前置依赖都已移除的顶点。弹出一个顶点并删去它的出边后,新变成入度 0 的顶点入队;若最终处理数小于 V,图中有环。adjacency list 实现为 O(V+E) 时间、O(V) 辅助空间。

  121. Tarjeta 121

    Pregunta

    用 queue 分层处理 frontier 时,怎样避免把下一层节点混入当前层?

    Respuesta

    在处理本层前先记录当前 queue size,只弹出这固定数量的对象。 过程中加入队尾的新对象属于下一层;不要让不断变化的 queue length 决定本层循环次数。

  122. Tarjeta 122

    Pregunta

    union-find 中,find(x) 返回值必须满足什么代表元不变量?

    Respuesta

    它返回 x 所在分量的根代表元 r,且 parent[r] = r。两个元素连通当且仅当它们的根相同;路径压缩只能缩短父链,不能改变分量归属。

  123. Tarjeta 123

    Pregunta

    为什么列举全部结果的回溯复杂度必须考虑输出规模?

    Respuesta

    因为仅写出结果就需要与输出字节数成正比的时间。更准确的口径是 O(访问状态数 × 每状态工作 + 输出规模);结果很多时,算法不可能比输出本身更快。

  124. Tarjeta 124

    Pregunta

    binary heap 没有 decrease-key 时,Dijkstra 怎样处理同一顶点的旧距离条目?

    Respuesta

    每次松弛成功就压入新 (distance, vertex);弹出时若距离不等于当前 dist[vertex],就跳过旧条目。adjacency list 上这种 lazy 实现为 O((V+E) log E) 时间、最坏 O(V+E) 辅助空间;简单图中也可写成 O((V+E) log V)

  125. Tarjeta 125

    Pregunta

    “输入位置互不相同”为什么不等于“生成结果按值互不重复”?

    Respuesta

    不同索引可以保存相同值。用索引判断是否已使用只能防止重复占用同一位置;若结果按值判等,还要处理等值候选造成的对称分支。

  126. Tarjeta 126

    Pregunta

    DFS 怎样生成 topological order?

    Respuesta

    在一个顶点的所有出邻居处理完后,把它加入 finish 列表,最后反转该列表。还要用三色状态检测有向环;有环时 finish 顺序不是有效拓扑序。

  127. Tarjeta 127

    Pregunta

    union by rank 或 size 为什么只能在两个根之间合并?

    Respuesta

    因为 rank 或 size 描述的是整棵代表树,只有根持有有效的分量摘要。先 find 两个根,再把较矮或较小的根挂到另一根下,才能保持结构浅且摘要正确。

  128. Tarjeta 128

    Pregunta

    哪类前缀最适合立即剪枝?

    Respuesta

    已经违反且后续选择无法修复的约束前缀。例如约束具有单调恶化性质时,一旦越界,继续加选择也不可能恢复合法。

  129. Tarjeta 129

    Pregunta

    从源点可达的边中出现负权时,为什么不能继续套用 Dijkstra?

    Respuesta

    因为一个已弹出的顶点仍可能经负权边得到更短路径,破坏“最小 tentative distance 已确定”的不变量。无负环时可考虑 Bellman–Ford;若存在源点可达的负环,从该环还能到达的顶点没有有限最短距离。

  130. Tarjeta 130

    Pregunta

    求最优值的回溯中,什么时候可以用上界或下界剪掉整个分支?

    Respuesta

    当该分支即使达到可证明的最好界,也不可能优于当前最优解时。界必须对该分支剩余可能性有效;界越紧通常剪得越多,但不会改变最坏情况保证。

  131. Tarjeta 131

    Pregunta

    为什么同一个 DAG 可能有多个合法 topological order?

    Respuesta

    没有依赖关系强制先后的顶点可以交换位置。Kahn 过程中若某一步同时有多个入度 0 的选择,就存在多个合法顺序;每一步都只有一个选择时,顺序才唯一。

  132. Tarjeta 132

    Pregunta

    同时使用路径压缩和按秩或大小合并时,union-find 操作的复杂度应怎样表述?

    Respuesta

    完成 O(n) 初始化后,mfind/union 总时间是 O(m α(n)),所以单次是摊还 O(α(n))α(n) 是增长极慢的反 Ackermann 函数,工程上近似常数,但不是严格 O(1)

  133. Tarjeta 133

    Pregunta

    搜索树每层最多 b 个候选、最大深度为 d 时,朴素回溯的规模怎样估算?

    Respuesta

    节点上界是 1+b+…+b^d;当 b>1 时为 O(b^d),当 b=1 时为 O(d)。若每个叶子还复制长度 d 的结果,要另计复制成本;实际分析优先使用真实访问状态数。

  134. Tarjeta 134

    Pregunta

    逐条加入简单无向图的边时,union-find 怎样识别一条形成环的冗余边?

    Respuesta

    加入 {u, v} 前若 find(u) = find(v),两点已经连通,这条新边就闭合了一个环;否则执行 union。这个判定针对增量式无向图,不能直接套到有向图。

  135. Tarjeta 135

    Pregunta

    当前和超过目标就停止向下搜索,需要什么前提?

    Respuesta

    需要后续选择不会让和下降,例如所有可追加值都非负。若还允许负值,超过目标的前缀仍可能被拉回,直接剪枝会漏解。

  136. Tarjeta 136

    Pregunta

    union-find 能回答连通性,却不能直接回答哪些常见图问题?

    Respuesta

    它不能恢复具体路径、计算最短距离或表达有向可达性,也不擅长普通在线删边。需要这些信息时,应保留图表示,并选择 BFS、DFS、最短路或更专门的动态结构。

  137. Tarjeta 137

    Pregunta

    不计输出时,深度为 d 的原地回溯通常需要多少辅助空间?

    Respuesta

    通常是 O(d) 调用栈,再加当前路径和约束状态所占空间。若每层复制完整状态,空间可能更高,不能只看递归深度。

  138. Tarjeta 138

    Pregunta

    什么组合信号最值得考虑动态规划?

    Respuesta

    问题可拆成重复出现的子问题,而且目标答案能由这些子问题答案组合出来。还需要能把每个子问题压成有限、可比较的 state;只有递归结构并不够。

  139. Tarjeta 139

    Pregunta

    某状态下只有一个合法候选时,应怎样处理这一步?

    Respuesta

    直接沿唯一候选继续即可。它仍属于搜索状态转换,但不会产生分支;提前识别 forced move 能减少通用候选循环的开销,也方便暴露无候选的失败状态。

  140. Tarjeta 140

    Pregunta

    写 DP 前,dp[...] 的含义应该明确到什么程度?

    Respuesta

    要明确每个索引代表的输入范围、资源或边界,以及值表示最大值、最小值、计数还是可行性。例如“处理前 i 个元素、容量为 c 时的最佳值”才足以推导 transition。

  141. Tarjeta 141

    Pregunta

    约束搜索中,为什么常优先处理候选最少的未决变量?

    Respuesta

    它更可能尽早暴露冲突,从而缩短失败分支。这是改变搜索顺序的启发式,不会单独证明更好的最坏复杂度,也不能删掉任何合法候选。

  142. Tarjeta 142

    Pregunta

    base case 在 DP 中除了终止递归,还承担什么作用?

    Respuesta

    它定义最小子问题的真实答案,并为所有后续 transition 提供起点。错误的 base case 会系统性污染整张表,即使递推式本身正确。

  143. Tarjeta 143

    Pregunta

    回溯中的 symmetry breaking 在删什么?

    Respuesta

    它删除的是由对称选择产生、但代表同一个本质结果的等价分支。规则必须为每个等价类保留至少一个代表,否则会把真正不同的解一起删掉。

  144. Tarjeta 144

    Pregunta

    怎样检查 DP state 是否足以代表子问题?

    Respuesta

    若两个历史映射到同一 state,它们未来的合法选择和最优后续值必须相同。若未来还依赖被丢掉的历史信息,state 就不充分,需要增加维度或改变定义。

  145. Tarjeta 145

    Pregunta

    一次选择后立即缩小其他变量的候选集合,为什么能加速回溯?

    Respuesta

    这是约束传播:它把新确定的信息尽早传给后续状态,让矛盾更早出现。撤销选择时也必须恢复被缩小的候选集合,或使用不可变的新状态。

  146. Tarjeta 146

    Pregunta

    为什么很多 DP 要显式定义空前缀、零容量或空区间?

    Respuesta

    这些是 transition 会引用的合法最小状态。把它们作为哨兵行、哨兵列或长度为零的 base case,能让边界与普通递推保持同一语义。

  147. Tarjeta 147

    Pregunta

    加入剪枝后,为什么不能直接宣称回溯从指数时间变成多项式时间?

    Respuesta

    因为剪枝效果依赖输入和界的强弱;仍可能出现几乎没有分支可剪的输入。除非能证明所有输入的访问状态数都有多项式上界,否则最坏复杂度仍可能是指数级。

  148. Tarjeta 148

    Pregunta

    DP transition 应从哪里推导,而不是靠套模板?

    Respuesta

    从最后一次选择或当前 state 的合法前驱推导。先覆盖所有合法来源,再用与目标一致的 minmax、求和或逻辑运算合并;计数时还要保证分类互斥。

  149. Tarjeta 149

    Pregunta

    只改变候选尝试顺序,会影响回溯的正确性吗?

    Respuesta

    完整枚举时不会,只要所有合法候选仍会被访问。寻找首个解或配合当前最优界时,好的顺序可能更早得到强基准,从而显著加快剪枝,但最坏情况未必改变。

  150. Tarjeta 150

    Pregunta

    tabulation 的迭代顺序由什么决定?

    Respuesta

    由依赖方向决定:计算一个 state 前,它引用的所有前驱必须已经完成。先画出 transition 的依赖边,再选相应的索引、长度或拓扑顺序。

  151. Tarjeta 151

    Pregunta

    枚举不允许重复节点的路径时,visited 为什么常要在回溯后撤销?

    Respuesta

    因为约束通常是“当前路径内不能重复”,而不是“所有路径全局不能再用”。离开当前分支后撤销标记,其他独立路径才有机会使用该节点。

  152. Tarjeta 152

    Pregunta

    memoization 怎样消除递归中的重复子问题?

    Respuesta

    第一次计算某个完整 state key 时保存结果,以后遇到同一 key 直接复用。它只访问从初始状态可达的子问题,但还会保留递归调用栈。

  153. Tarjeta 153

    Pregunta

    什么时候可以给回溯加入 memo,而不把不同历史错误合并?

    Respuesta

    当返回结果只由 memo key 表示的状态决定时。若合法后续还取决于未编码的路径历史,缓存会复用错误答案;若目标是逐条输出路径,也不能把聚合值缓存当成完整枚举。

  154. Tarjeta 154

    Pregunta

    tabulation 的核心做法是什么?

    Respuesta

    按合法依赖顺序从 base case 向目标 state 填表。它不需要递归栈,通常布局紧凑,但可能计算一些从实际初始输入不可达的表项。

  155. Tarjeta 155

    Pregunta

    什么时候把 visited 设为全局且不撤销会改变搜索语义?

    Respuesta

    当同一状态可由不同路径到达,而目标需要区分或枚举这些路径时。全局标记会合并后来的到达;只有“每个状态处理一次”足以完成目标时才适合不撤销。

  156. Tarjeta 156

    Pregunta

    状态空间稀疏且很多 state 不可达时,memoization 与 tabulation 通常怎样取舍?

    Respuesta

    memoization 常更自然,因为只计算可达 state;tabulation 可能扫完整稠密表。若递归深度危险、依赖顺序清晰或需要紧凑连续内存,tabulation 可能更合适。

  157. Tarjeta 157

    Pregunta

    回溯状态应该每层复制,还是原地修改后撤销?

    Respuesta

    两者都可以。复制更容易保持分支隔离但每层可能付出状态大小的时间和空间;原地修改更省复制成本,却要求每个变化都有严格对称的撤销。

  158. Tarjeta 158

    Pregunta

    DP 时间复杂度应怎样从 state 与 transition 推导?

    Respuesta

    可达或已填的 state 数 S × 每个 state 的 transition 成本 T,即 O(S·T)。若不同 state 的出边数不同,更准确地把所有实际 transition 成本求和。

  159. Tarjeta 159

    Pregunta

    当前没有合法候选,但结果还不完整时,这个回溯状态代表什么?

    Respuesta

    它代表失败叶子,应直接返回且不收集结果。不要把“无法继续”与“已经完成”混成同一个终止条件。

  160. Tarjeta 160

    Pregunta

    分析 top-down DP 的辅助空间时,要统计哪些部分?

    Respuesta

    统计最多 S 个缓存 state、最大递归深度 h 的调用栈,以及每层或每个 state 保存的额外数据。因此常见口径是 O(S + h),但大对象 key 或 parent 表还要另计。

  161. Tarjeta 161

    Pregunta

    搜索树可能非常深时,递归回溯还有什么实现风险?

    Respuesta

    调用栈可能超出运行时限制。可改用显式栈保存状态与下一候选位置,但仍要准确模拟进入、探索和撤销;这只改变栈的承载方式,不降低搜索规模。

  162. Tarjeta 162

    Pregunta

    普通 DP recurrence 的依赖图有环时,为什么不能直接递归加 memo?

    Respuesta

    因为某个 state 会在结果确定前再次依赖自己,memo 中还没有可复用的完成值。需要证明能消除环、改写 state,或使用适合循环依赖的其他算法,而不是把进行中的值当答案。

  163. Tarjeta 163

    Pregunta

    什么信号说明朴素回溯很可能无法承受输入规模?

    Respuesta

    分支因子大、深度高、约束弱,而且不存在可复用的紧凑子状态。此时访问状态可能接近完整决策树规模;应寻找更强剪枝、可合并的 DP 状态或问题特有结构。

  164. Tarjeta 164

    Pregunta

    什么时候自然地把 1D DP state 定义为 dp[i]

    Respuesta

    当前答案只需描述前 i 个元素、长度 i 的前缀或位置 i,且未来不需要更多历史维度时。必须先说清 i 是数量还是索引,避免 base case 与返回位置错一位。

  165. Tarjeta 165

    Pregunta

    什么题目信号值得考虑贪心,但还不足以证明贪心正确?

    Respuesta

    解由一串可立即做出的局部选择组成,而且希望每步只保留一个方向。这个结构只提示候选方法;还必须证明当前选择不会排除某个全局最优解。

  166. Tarjeta 166

    Pregunta

    网格型 DP 中,dp[r][c] 最常表示哪类子问题?

    Respuesta

    表示到达格点 (r,c) 的答案,或以该格点为边界的矩形前缀答案。具体含义必须说明允许从哪些方向转移;坐标本身不能替代 transition 定义。

  167. Tarjeta 167

    Pregunta

    贪心选择性质具体要说明什么?

    Respuesta

    要说明存在一个最优解包含当前贪心选择。这样做出该选择后,剩余部分仍可作为同类子问题继续求解,而不是仅凭局部指标看起来最好。

  168. Tarjeta 168

    Pregunta

    两个序列共同决定答案时,dp[i][j] 的自然语义是什么?

    Respuesta

    通常表示第一个序列前 i 个元素与第二个序列前 j 个元素组成的子问题。比较当前末尾后,transition 可引用缩短一个或两个前缀的 state。

  169. Tarjeta 169

    Pregunta

    exchange argument 怎样证明第一步贪心选择是安全的?

    Respuesta

    取一个最优解,把它与贪心选择冲突的部分换掉,并证明可行性不变且目标值不变差。于是至少有一个同样好的最优解以贪心选择开头。

  170. Tarjeta 170

    Pregunta

    资源容量限制下的选择型 DP,常用哪两个维度定义 state?

    Respuesta

    常用“已考虑的前 i 个候选”和“当前容量 c”。dp[i][c] 表示在这两项限制下的目标值,使“不选当前项”和“选择当前项”能引用明确前驱。

  171. Tarjeta 171

    Pregunta

    staying-ahead 证明比较的是什么?

    Respuesta

    比较贪心解与任意最优解的每个等长前缀,证明贪心前缀在关键指标上始终不落后。最后一个前缀就是完整解,因此得到整体最优性。

  172. Tarjeta 172

    Pregunta

    当答案由连续区间内部的拆分决定时,常用什么 DP state?

    Respuesta

    使用 dp[l][r] 表示区间 [l,r] 的答案。transition 枚举合法分割点或最后合并位置,并组合更短的子区间。

  173. Tarjeta 173

    Pregunta

    用归纳法证明贪心时,归纳步骤要连接哪两个事实?

    Respuesta

    先证明当前选择属于某个最优解,再证明选完后的剩余实例仍满足同样结构。这样可对剩余实例重复应用贪心论证。

  174. Tarjeta 174

    Pregunta

    DAG 上的 DP 应按什么顺序处理节点?

    Respuesta

    按与 transition 依赖方向一致的拓扑顺序。若 u 的答案用于更新 v,就先完成 u;反向定义 state 时则使用反向拓扑顺序。

  175. Tarjeta 175

    Pregunta

    为什么只有 optimal substructure 不能推出贪心正确?

    Respuesta

    因为它只说明最优解由最优子解组成,没有说明哪一个局部选择可安全固定。多个分支都可能通向不同子问题;若无法排除分支,通常仍需 DP 或搜索比较它们。

  176. Tarjeta 176

    Pregunta

    计数型 DP 为什么常把前驱答案相加?

    Respuesta

    因为每个前驱代表一组到达当前 state 的方案;当这些方案集合互不重叠且覆盖全部情况时,总数就是求和。若分类重叠,相加会重复计数。

  177. Tarjeta 177

    Pregunta

    exchange argument 只证明能交换第一处差异,为什么还不够?

    Respuesta

    还要说明交换可反复进行,最终能把某个最优解转换成完整的贪心解。若后续交换会破坏前面保持的结构,证明链就没有闭合。

  178. Tarjeta 178

    Pregunta

    1D recurrence 只依赖前 k 个 state 时,空间可怎样压缩?

    Respuesta

    只保留长度 k 的滚动窗口,把空间从 O(n) 降到 O(k);若 k 是常数,就是 O(1)。更新前要保存仍会被后续 transition 使用的旧值。

  179. Tarjeta 179

    Pregunta

    怀疑某个贪心规则错误时,一个直接的快速检查是什么?

    Respuesta

    构造一个最小反例,让局部最优选择占用未来关键资源,而另一项当前略差的选择得到更好总结果。一个合法反例就足以否定通用正确性。

  180. Tarjeta 180

    Pregunta

    网格 DP 的第一行和第一列为什么经常需要单独初始化?

    Respuesta

    它们缺少某些普通前驱,直接套通用 transition 会越界或读到假状态。可以单独初始化,也可加语义明确的哨兵边界;两种方法都要保持不可达位置不可被误用。

  181. Tarjeta 181

    Pregunta

    贪心算法先排序时,排序依据应怎样论证?

    Respuesta

    常见办法是证明把相邻逆序对换成该顺序不会破坏可行性或使目标变差,并能反复交换到目标顺序。若只能说这个 key “直觉上重要”,排序仍是未经证明的猜测。

  182. Tarjeta 182

    Pregunta

    长度分别为 nm 的双序列 DP,若每个 state 做 O(1) 工作,复杂度是多少?

    Respuesta

    共有 O(nm) 个 state,因此时间为 O(nm),完整表空间为 O(nm)。若只依赖有限相邻行,可按列数压缩;只有序列角色可交换时,才能进一步选短序列为列并写成 O(min(n,m))

  183. Tarjeta 183

    Pregunta

    “每次拿当前收益最大项”为什么可能失败?

    Respuesta

    当前最大收益可能消耗过多容量或阻塞多个稍小但合计更优的选择。除非能证明替换或支配关系,单步收益最大只是启发式。

  184. Tarjeta 184

    Pregunta

    “每个候选选或不选”的 DP transition 怎样组织?

    Respuesta

    把当前 state 的答案合并为两类:不选时继承前一个候选层;可选时从扣除相应资源的合法前驱加上当前贡献。两类必须覆盖全部方案且不重复。

  185. Tarjeta 185

    Pregunta

    排序后单次贪心扫描通常需要维护什么不变量?

    Respuesta

    已处理前缀的选择必须始终可行,并且能扩展成某个该前缀下不劣的完整解。每次接受或拒绝候选都要保持这个性质。

  186. Tarjeta 186

    Pregunta

    bottom-up interval DP 为什么通常按区间长度递增填表?

    Respuesta

    因为长区间的 transition 通常依赖更短子区间。先完成长度较小的 [l,r],再扩展长度,能保证所有前驱已经有值。

  187. Tarjeta 187

    Pregunta

    同一个最优化场景中,什么区别通常把贪心与 DP 分开?

    Respuesta

    贪心能证明一个局部选择可永久固定;DP 则保留多个状态下的最佳结果,直到 transition 比较后再决定。无法证明安全丢弃其他选择时,更像 DP。

  188. Tarjeta 188

    Pregunta

    DAG DP 每条依赖边处理一次时,复杂度怎样计算?

    Respuesta

    V 个 state 和 E 条 transition 边,拓扑排序加递推通常是 O(V+E) 时间;邻接表、入度和 DP 值共需 O(V+E) 空间。

  189. Tarjeta 189

    Pregunta

    贪心指标相同时,tie-breaking 可以随便写吗?

    Respuesta

    只有证明任一并列选择都安全时才可以。否则第二关键字可能影响可行性或未来选择;实现中应明确稳定、确定且符合证明的 tie-breaking。

  190. Tarjeta 190

    Pregunta

    可行性 DP 的多个前驱通常怎样合并?

    Respuesta

    用逻辑 OR:只要存在一个合法前驱能转移到当前 state,它就可达。初始化时必须区分真实可达的 base case 与默认的 false

  191. Tarjeta 191

    Pregunta

    按收益与成本比选择时,“资源可切分”为什么是重要边界?

    Respuesta

    可切分时,剩余容量可用候选的一部分填补;不可切分时,一个高比率大项可能挡住组合起来更好的小项。比率规则能否精确求优必须按具体约束证明。

  192. Tarjeta 192

    Pregunta

    把双序列 DP 压成一行时,为什么常需要保存左上角旧值?

    Respuesta

    因为原 dp[i-1][j-1] 会在原地更新 dp[j] 时被覆盖。用临时变量保存更新前的左上角,才能同时读取上一行、当前行左侧和上一行左侧。

  193. Tarjeta 193

    Pregunta

    贪心前的排序或去除被支配项,属于算法复杂度的一部分吗?

    Respuesta

    属于。若 n 个候选先 comparison sort 再线性扫描,时间是 O(n log n),不是 O(n);排序辅助空间取决于具体实现。

  194. Tarjeta 194

    Pregunta

    每个候选最多使用一次时,1D 容量 DP 为什么通常倒序更新 c

    Respuesta

    倒序保证读取的 dp[c-cost] 仍来自处理当前候选之前的旧层,因此当前候选不会在同一轮被重复使用。正序会读到本轮刚更新的值,改变问题语义。

  195. Tarjeta 195

    Pregunta

    什么时候可以用 dominance rule 永久丢弃一个候选?

    Respuesta

    当另一个候选在所有与未来有关的维度上都不差,并至少一项更好,而且替换后仍可行。只比较一个维度不足以建立支配关系。

  196. Tarjeta 196

    Pregunta

    长度为 n 的 interval DP 若每个区间枚举 O(n) 个分割点,时间和表空间是多少?

    Respuesta

    共有 O(n²) 个区间 state,每个最多做 O(n) 次 transition,所以时间为 O(n³),完整表空间为 O(n²)

  197. Tarjeta 197

    Pregunta

    若候选已按证明所需顺序给出,贪心扫描何时是 O(n)

    Respuesta

    n 个候选各处理一次,且每次可行性更新和选择都是 O(1) 时,扫描为 O(n)。若更新依赖树、heap 或再次搜索,就要把相应成本计入。

  198. Tarjeta 198

    Pregunta

    最小值或最大值 DP 中,不可达 state 应怎样初始化?

    Respuesta

    用不会被误当成合法答案的 sentinel,例如最小化用 +∞、最大化用 -∞,并只从可达前驱转移。随手初始化为 0 可能凭空制造路径。

  199. Tarjeta 199

    Pregunta

    选择不可撤销且非法前缀无法修复时,贪心每一步要检查什么?

    Respuesta

    检查加入候选后前缀仍然可行。一旦接受非法选择,后续无法修复;检查所用的状态和约束必须与正确性证明一致。

  200. Tarjeta 200

    Pregunta

    正成本候选允许重复使用时,1D 容量 DP 为什么常正序更新 c

    Respuesta

    正序让 dp[c-cost] 可以是本轮刚加入当前候选后的值,从而再次使用它。这个方向只适用于 transition 明确允许无限复用的语义;零成本候选要单独判断是否使目标无界。

  201. Tarjeta 201

    Pregunta

    用大小最多为 k 的 heap 从 n 个候选中保留当前最佳集合,复杂度怎样计算?

    Respuesta

    若每个候选至多触发常数次 heap 操作,时间为 O(n log k),辅助空间为 O(k)。这只是实现成本;保留哪些候选仍需要独立的正确性证明。

  202. Tarjeta 202

    Pregunta

    计数 DP 结果可能很大时,什么时候可以在每次 transition 后取模?

    Respuesta

    只有题目要求返回某个模数下的结果,且后续运算与模运算兼容时。若需要精确计数、比较真实大小或恢复值,擅自取模会改变答案。

  203. Tarjeta 203

    Pregunta

    一个离线贪心证明为什么不能自动用于在线到达的候选?

    Respuesta

    离线算法可能依赖完整排序或未来信息,在线算法在做决定时看不到这些内容。若选择不可撤销,信息条件变化会破坏原来的安全选择论证。

  204. Tarjeta 204

    Pregunta

    n 个候选、整数容量上限 C 的典型容量 DP,复杂度怎样表达?

    Respuesta

    若每个 (i,c) state 做 O(1) transition,时间为 O(nC);完整二维表空间为 O(nC),可合法滚动时为 O(C)。这是 pseudo-polynomial:C 的数值可能远大于其编码长度。

  205. Tarjeta 205

    Pregunta

    输入允许负收益或负成本时,为什么要重新检查原有贪心规则?

    Respuesta

    因为许多单调性、排序和“加入不会变差”的论证默认量为非负。负值不必然使贪心失败,但它可能直接破坏证明依赖的前提。

  206. Tarjeta 206

    Pregunta

    DP 表只有最优值时,怎样恢复一条具体最优选择序列?

    Respuesta

    为每个 state 记录产生最优值的 parent 或选择,最后从目标 state 反向追踪到 base case。若只需一条路径,并列最优时使用确定的 tie-breaking 即可。

  207. Tarjeta 207

    Pregunta

    把“最小化”贪心规则反向排序,是否就能解决对应的“最大化”目标?

    Respuesta

    不能直接推断。目标方向改变后,可行替换和前缀支配关系也可能改变;反向规则需要自己的安全选择证明或反例检查。

  208. Tarjeta 208

    Pregunta

    1D DP 压缩后出现“同一个候选被意外重复使用”,最先检查什么?

    Respuesta

    检查内层索引方向。原地数组同时承载上一层和当前层;错误方向会让 transition 读到本轮刚写入的值,从而改变“每项一次”或“可重复”的语义。

  209. Tarjeta 209

    Pregunta

    为什么给一个已证明的贪心问题增加新约束后,必须重做证明?

    Respuesta

    新约束可能让原来的交换不再可行,或让局部选择消耗此前不存在的资源。算法代码即使还能运行,也不代表最优性仍成立。

  210. Tarjeta 210

    Pregunta

    top-down DP 在小输入正确、复杂输入偶尔错误时,memo key 应检查什么?

    Respuesta

    检查 key 是否包含所有会影响未来答案的状态变量。若不同剩余资源、边界或约束共享同一个 key,后计算的分支会错误复用先前结果。

  211. Tarjeta 211

    Pregunta

    尚无最优性证明或可用定理依据的局部选择算法,应怎样准确描述?

    Respuesta

    应暂时描述为启发式,而不是精确贪心解法。它可能在常见输入上表现好,但不能承诺总是得到全局最优结果。

  212. Tarjeta 212

    Pregunta

    为什么 DP 能处理局部最优选择不安全的场景?

    Respuesta

    DP 不立即固定单一路径,而是为不同 state 保留各自的最佳答案,再通过 transition 比较。它用更多 state 和计算换取对多种未来可能性的保留。

  213. Tarjeta 213

    Pregunta

    贪心算法不能保证精确最优时,还可能证明什么有用性质?

    Respuesta

    可能证明 approximation bound,即结果与最优值之间的最坏比例或加性差距。这个界必须单独证明,不能由“用了贪心”自动得到。

  214. Tarjeta 214

    Pregunta

    memoized backtracking 在什么情况下本质上就是 top-down DP?

    Respuesta

    当搜索返回的是可聚合的最优值、计数或可行性,而且未来只由紧凑 state key 决定时。若必须区分并输出每条历史路径,单纯合并同 state 就不等价。

  215. Tarjeta 215

    Pregunta

    贪心只维护目标值时,怎样在最后返回具体选择?

    Respuesta

    在接受候选时同步记录其标识或 parent 关系。若之后允许替换,就同时更新记录;只保存聚合值通常无法唯一恢复选择集合。

  216. Tarjeta 216

    Pregunta

    什么时候可以安全压缩 DP 空间?

    Respuesta

    当未来 transition 只依赖一个有限窗口内的旧 state,且更新顺序不会提前覆盖仍需读取的值。先列出依赖,再决定滚动维度;不能只因表很大就压缩。

深蓝色背景上,中央光点连接双指针、滑动窗口、树、图、搜索路径和状态网格的抽象图形

216 tarjetas

算法面试模式闪卡:双指针、滑动窗口、图与动态规划

Estudia este mazo gratis

Flashcards se abrirá para que puedas empezar a estudiar.