<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>刷题 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E5%88%B7%E9%A2%98/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Thu, 02 Oct 2025 15:10:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E5%88%B7%E9%A2%98/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《labuladong 的算法小抄》学习笔记：刷题先学框架，算法本质是穷举</title>
      <link>https://ftmao.com/blog/posts/labuladong-algorithm-cheatsheet-book-notes/</link>
      <pubDate>Thu, 02 Oct 2025 15:10:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/labuladong-algorithm-cheatsheet-book-notes/</guid>
      <description>&lt;h1 id=&#34;labuladong-的算法小抄学习笔记刷题先学框架算法本质是穷举&#34;&gt;《labuladong 的算法小抄》学习笔记：刷题先学框架，算法本质是穷举&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：正在刷 LeetCode（或准备刷）、刷过几十题但换个马甲就认不出来的程序员；面试季突击者尤佳。&lt;br&gt;
参考书籍：labuladong（付东来）《labuladong 的算法小抄》（电子工业出版社，2020），内容源自其开源项目（GitHub 上的算法教程仓库）。&lt;br&gt;
阅读目标：提炼全书的&amp;quot;框架思维&amp;quot;——不背题，只背框架；认清每类题目的本质穷举结构，剩下的都是填空。本文在笔记之外补充了每个框架的典型原题、解题思路和关键代码（Java），代码均按书里的框架写法整理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面一本反算法书的算法书&#34;&gt;0. 写在前面：一本&amp;quot;反算法书&amp;quot;的算法书&lt;/h2&gt;
&lt;p&gt;这本书的出身就不同寻常：它不是教授写的教科书，而是一个刷过几百道 LeetCode 的工程师，把自己&amp;quot;打怪升级&amp;quot;的套路整理成的实战手册。先在网上开源（当年以 repo 名的粗口出圈），火了之后才出的书。&lt;/p&gt;
&lt;p&gt;所以它跟《图解算法》正好互补（本站的&lt;a href=&#34;https://ftmao.com/blog/blog/posts/grokking-algorithms-book-notes/&#34;&gt;《图解算法》读书笔记&lt;/a&gt;可以对照着看）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《图解算法》是&lt;strong&gt;科普&lt;/strong&gt;：让你知道世界上有哪些工具，各自干什么；&lt;/li&gt;
&lt;li&gt;《算法小抄》是&lt;strong&gt;教练手册&lt;/strong&gt;：假设你已经知道工具，教你&amp;quot;看到一道新题，30 秒内判断它属于哪个框架，然后往里填&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;全书的核心主张，用作者自己的话说：&lt;strong&gt;算法的本质就是穷举，所有的算法技巧都是&amp;quot;如何聪明地穷举&amp;quot;&lt;/strong&gt;。动态规划靠备忘录消除重复穷举，回溯靠剪枝砍掉无效穷举，双指针靠单调性跳过不可能的穷举。&lt;/p&gt;
&lt;p&gt;这句话是理解全书的钥匙。下面的每一节，都是在回答同一个问题：&amp;ldquo;这一类题，穷举的结构长什么样？&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-全书总纲数据结构只有两种算法就是遍历&#34;&gt;1. 全书总纲：数据结构只有两种，算法就是遍历&lt;/h2&gt;
&lt;p&gt;开篇第一章是全书的&amp;quot;世界观&amp;quot;，作者把纷繁复杂的数据结构和算法压成两句话。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一句：数据结构只有两种底层存储方式——数组（顺序存储）和链表（链式存储）。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;散列表、栈、队列、堆、树、图，全是这两者的排列组合：散列表是&amp;quot;数组 + 散列函数&amp;quot;（拉链法还叠加链表），队列/栈用数组或链表都能实现，图用邻接表就是&amp;quot;数组套链表&amp;quot;，二叉树是链表的升级（一个节点带两个指针）。所以&lt;strong&gt;数据结构没有新东西，只有新组合&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二句：数据结构的基本操作只有&amp;quot;遍历 + 访问&amp;quot;，而遍历只有两种形式——线性的（for/while 迭代）和非线性的（递归）。&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;所有算法题&amp;#34;] --&amp;gt; B[&amp;#34;本质是穷举&amp;#34;]
    B --&amp;gt; C[&amp;#34;线性结构&amp;lt;br/&amp;gt;用迭代遍历&amp;lt;br/&amp;gt;双指针 滑动窗口 前缀和&amp;#34;]
    B --&amp;gt; D[&amp;#34;非线性结构&amp;lt;br/&amp;gt;用递归遍历&amp;lt;br/&amp;gt;DFS 回溯 动态规划&amp;#34;]
    B --&amp;gt; E[&amp;#34;图与树&amp;lt;br/&amp;gt;层序遍历&amp;lt;br/&amp;gt;BFS&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;由此推出全书最重要的暗线：&lt;strong&gt;一切递归皆树遍历&lt;/strong&gt;。你写的任何递归函数，其执行过程都等价于在遍历一棵隐形的树——调用关系构成树，函数体里的代码位置对应树的前序/中序/后序位置。动态规划、回溯、分治，全是这棵&amp;quot;递归树&amp;quot;上的不同走法。&lt;/p&gt;
&lt;p&gt;这个视角的威力在于：它把&amp;quot;动态规划好难&amp;quot;这种情绪问题，降级成&amp;quot;这棵树怎么遍历&amp;quot;的技术问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-动态规划带备忘录的穷举&#34;&gt;2. 动态规划：带备忘录的穷举&lt;/h2&gt;
&lt;p&gt;全书篇幅最重的部分，作者也自认是招牌。动态规划在书里被还原成一句话：&lt;strong&gt;求最值的问题，穷举所有可行解找最优，发现有重叠子问题，就加备忘录或 DP 表消除重复。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三要素&lt;/strong&gt;（判断一个问题能不能用 DP）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;重叠子问题&lt;/strong&gt;：暴力递归会反复计算同一个子问题。这是&amp;quot;值得优化&amp;quot;的信号。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最优子结构&lt;/strong&gt;：大问题的最优解能由子问题的最优解推出。这是&amp;quot;能够分解&amp;quot;的依据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;状态转移方程&lt;/strong&gt;：子问题之间的数学关系，是 DP 的&amp;quot;最难也唯一难&amp;quot;的部分。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;书里给的标准解题流程，值得背下来：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;明确 状态&amp;lt;br/&amp;gt;原问题和子问题中&amp;lt;br/&amp;gt;会变化的量&amp;#34;] --&amp;gt; B[&amp;#34;定义 dp 数组或函数&amp;lt;br/&amp;gt;它的返回值是什么含义&amp;#34;]
    B --&amp;gt; C[&amp;#34;明确 选择&amp;lt;br/&amp;gt;每个状态有哪些岔路&amp;#34;]
    C --&amp;gt; D[&amp;#34;明确 base case&amp;lt;br/&amp;gt;最小的子问题直接给答案&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;21-入门例题斐波那契重叠子问题长什么样&#34;&gt;2.1 入门例题：斐波那契——重叠子问题长什么样&lt;/h3&gt;
&lt;p&gt;暴力递归的 &lt;code&gt;fib(n) = fib(n-1) + fib(n-2)&lt;/code&gt; 人人会写，但它的执行过程是一棵二叉树，同一棵子树被反复展开：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;fib(5)&amp;#34;] --&amp;gt; B[&amp;#34;fib(4)&amp;#34;]
    A --&amp;gt; C[&amp;#34;fib(3)&amp;#34;]
    B --&amp;gt; D[&amp;#34;fib(3)&amp;lt;br/&amp;gt;重复计算&amp;#34;]
    B --&amp;gt; E[&amp;#34;fib(2)&amp;#34;]
    C --&amp;gt; F[&amp;#34;fib(2)&amp;lt;br/&amp;gt;重复计算&amp;#34;]
    C --&amp;gt; G[&amp;#34;fib(1)&amp;#34;]
    D -.-&amp;gt;|&amp;#34;本子树 C 已经算过&amp;#34;| C&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;节点数是指数级的，时间复杂度 O(2^n)。优化思路朴素得惊人：&lt;strong&gt;算过的子问题存起来，下次直接查表&lt;/strong&gt;——这就是备忘录：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; memo; &lt;span style=&#34;color:#75715e&#34;&gt;// memo[n] 缓存 fib(n)，0 表示未计算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fib&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; 1) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; n;          &lt;span style=&#34;color:#75715e&#34;&gt;// base case&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; 0) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 查表，砍掉整棵重复子树&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fib(n &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; fib(n &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 2);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;加了一个数组，复杂度从 O(2^n) 降到 O(n)。&lt;strong&gt;动态规划的一切技巧，都是这个&amp;quot;备忘录&amp;quot;思想的变体&lt;/strong&gt;——备忘录是自顶向下查表，DP 表是自底向上填表，两者是同一棵递归树的两条遍历顺序。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;术语澄清：什么是 DP 表？&lt;/strong&gt; &amp;ldquo;DP&amp;rdquo; 是 Dynamic Programming（动态规划）的缩写，&lt;strong&gt;DP 表就是存子问题答案的那个数组&lt;/strong&gt;——每个格子对应一个子问题，格子的值就是它的答案。它和备忘录（memo）存的内容完全一样，区别只在&lt;strong&gt;计算顺序&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;备忘录&lt;/strong&gt;：自顶向下写递归时用的缓存。递归过程中算出一个子问题就顺手存进去，下次遇到直接查——&amp;ldquo;用到才算&amp;rdquo;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DP 表&lt;/strong&gt;：自底向上迭代时填的数组。从最小的 base case 出发，按依赖顺序把每个格子提前算好——&amp;ldquo;按顺序全算好&amp;rdquo;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以下文凡说&amp;quot;dp 数组&amp;quot;&amp;ldquo;dp 表&amp;rdquo;，指的都是这张存答案的表；&lt;strong&gt;定义 dp 含义，就是回答&amp;quot;这张表的每个格子代表哪个子问题的答案&amp;quot;&lt;/strong&gt;——这是 DP 四步流程里最关键的一步。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;22-招牌例题凑零钱leetcode-322&#34;&gt;2.2 招牌例题：凑零钱（LeetCode 322）&lt;/h3&gt;
&lt;p&gt;给你面额数组 &lt;code&gt;coins&lt;/code&gt; 和总金额 &lt;code&gt;amount&lt;/code&gt;，求凑出该金额的&lt;strong&gt;最少&lt;/strong&gt;硬币数，凑不出返回 -1。&lt;/p&gt;
&lt;p&gt;按四步走：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;状态&lt;/strong&gt;：目标金额 &lt;code&gt;n&lt;/code&gt;（唯一会变的量）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dp 定义&lt;/strong&gt;：&lt;code&gt;dp(n)&lt;/code&gt; 是凑出金额 &lt;code&gt;n&lt;/code&gt; 需要的最少硬币数；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;选择&lt;/strong&gt;：遍历每种面额，选一枚，问题规模缩小为 &lt;code&gt;n - coin&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;base case&lt;/strong&gt;：&lt;code&gt;dp(0) = 0&lt;/code&gt;，&lt;code&gt;n &amp;lt; 0&lt;/code&gt; 返回 -1 表示凑不出。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先写带备忘录的自顶向下版本（好写、不易错）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; memo;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;coinChange&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; coins, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; amount) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    memo &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;amount &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;fill&lt;/span&gt;(memo, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;2); &lt;span style=&#34;color:#75715e&#34;&gt;// -2 表示&amp;#34;未计算&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dp(coins, amount);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dp&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; coins, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; 0) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; 0;              &lt;span style=&#34;color:#75715e&#34;&gt;// base case&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; 0) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1;              &lt;span style=&#34;color:#75715e&#34;&gt;// 凑不出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;2) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 查备忘录，消除重复穷举&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Integer.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAX_VALUE&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; coin : coins) {           &lt;span style=&#34;color:#75715e&#34;&gt;// 选择：每种面额的硬币&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; sub &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dp(coins, n &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; coin);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (sub &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;min&lt;/span&gt;(res, sub &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (res &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; Integer.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAX_VALUE&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1 : res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; memo&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;再改自底向上的 DP 表（省递归栈空间）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;coinChange&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; coins, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; amount) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; dp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;amount &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;fill&lt;/span&gt;(dp, amount &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1); &lt;span style=&#34;color:#75715e&#34;&gt;// 初始化为&amp;#34;不可能的大值&amp;#34;充当正无穷&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;                   &lt;span style=&#34;color:#75715e&#34;&gt;// base case&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; amount; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; coin : coins) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; coin &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; 0) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;min&lt;/span&gt;(dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;, dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; coin&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;amount&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; amount &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1 &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1 : dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;amount&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;两个版本一一对照：&lt;strong&gt;递归的 base case 就是 dp[0] 的初值，递归的&amp;quot;选择&amp;quot;循环就是填表的内层循环，备忘录就是 dp 数组本身&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;23-系列一0-1-背包状态升维&#34;&gt;2.3 系列一：0-1 背包（状态升维）&lt;/h3&gt;
&lt;p&gt;有 &lt;code&gt;n&lt;/code&gt; 件物品，第 &lt;code&gt;i&lt;/code&gt; 件重 &lt;code&gt;wt[i]&lt;/code&gt;、值 &lt;code&gt;val[i]&lt;/code&gt;，背包容量 &lt;code&gt;W&lt;/code&gt;，每件只能选一次，求能装的最大价值。&lt;/p&gt;
&lt;p&gt;关键在于：这里&lt;strong&gt;会变化的量有两个&lt;/strong&gt;——&amp;ldquo;还剩哪些物品可选&amp;quot;和&amp;quot;背包还剩多少容量&amp;rdquo;，所以状态是二维的，dp 表自然是二维的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dp[i][w]&lt;/code&gt;：只考虑前 &lt;code&gt;i&lt;/code&gt; 件物品、容量为 &lt;code&gt;w&lt;/code&gt; 时的最大价值；&lt;/li&gt;
&lt;li&gt;对第 &lt;code&gt;i&lt;/code&gt; 件物品，选择只有两种：装或不装。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;knapsack&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; wt, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; val, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; W) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; wt.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[][]&lt;/span&gt; dp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;W &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 第 0 行第 0 列全为 0，即 base case&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; w &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; w &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; W; w&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (w &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; wt&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; 0) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;w&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;w&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// 装不下，只能不选&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;w&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;w&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;,                          &lt;span style=&#34;color:#75715e&#34;&gt;// 不选第 i 件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;w &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; wt&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; val&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 选第 i 件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;W&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;拿 &lt;code&gt;wt = [1, 3, 4]&lt;/code&gt;、&lt;code&gt;val = [15, 20, 30]&lt;/code&gt;、&lt;code&gt;W = 4&lt;/code&gt; 填一遍表，状态转移就一目了然（每格 = &amp;ldquo;不选它&amp;rdquo; 与 &amp;ldquo;选它&amp;rdquo; 的较大者）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;dp&lt;/th&gt;
					&lt;th&gt;w=0&lt;/th&gt;
					&lt;th&gt;w=1&lt;/th&gt;
					&lt;th&gt;w=2&lt;/th&gt;
					&lt;th&gt;w=3&lt;/th&gt;
					&lt;th&gt;w=4&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;i=0（无物品）&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;i=1（重1值15）&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;i=2（重3值20）&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;20&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;35&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;i=3（重4值30）&lt;/td&gt;
					&lt;td&gt;0&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;20&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;35&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;压缩成一维数组时，&lt;strong&gt;容量必须倒序遍历&lt;/strong&gt;（&lt;code&gt;w&lt;/code&gt; 从 &lt;code&gt;W&lt;/code&gt; 往小走），保证 &lt;code&gt;dp[w - wt[i]]&lt;/code&gt; 取到的还是&amp;quot;上一行&amp;quot;的旧值——每件物品只被选一次。这是 01 背包最常考的实现细节。&lt;/p&gt;
&lt;h3 id=&#34;24-系列二子序列问题双指针撞在表上&#34;&gt;2.4 系列二：子序列问题（双指针撞在表上）&lt;/h3&gt;
&lt;p&gt;编辑距离、最长公共子序列、最长回文子序列，套路一致：&lt;strong&gt;两个指针分别从两个字符串的尾部往前走，dp 表二维，指针的每种走法对应表里的一个相邻格子&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;以**编辑距离（LeetCode 72：给两个字符串 s1、s2，允许插入、删除、替换三种操作，求把 s1 变成 s2 的最少操作次数）**为例：&lt;code&gt;dp[i][j]&lt;/code&gt; 表示 &lt;code&gt;s1&lt;/code&gt; 的前 &lt;code&gt;i&lt;/code&gt; 个字符变成 &lt;code&gt;s2&lt;/code&gt; 的前 &lt;code&gt;j&lt;/code&gt; 个字符所需的最少操作数。指针在 &lt;code&gt;i, j&lt;/code&gt; 处时，如果字符相同就白走一步（&lt;code&gt;dp[i-1][j-1]&lt;/code&gt;），否则三种操作对应三个相邻格子：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;minDistance&lt;/span&gt;(String s1, String s2) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s1.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;(), n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s2.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[][]&lt;/span&gt; dp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;m &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;n &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; m; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; i; &lt;span style=&#34;color:#75715e&#34;&gt;// base case：s2 为空，删光&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; j&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; j; &lt;span style=&#34;color:#75715e&#34;&gt;// base case：s1 为空，插满&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; m; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; n; j&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (s1.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; s2.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(j &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;        &lt;span style=&#34;color:#75715e&#34;&gt;// 字符相同，什么都不做&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;min&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;min&lt;/span&gt;(dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1,      &lt;span style=&#34;color:#75715e&#34;&gt;// 删：i 指针前移&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1),     &lt;span style=&#34;color:#75715e&#34;&gt;// 插：j 指针前移&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1            &lt;span style=&#34;color:#75715e&#34;&gt;// 改：两个指针都前移&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;m&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;**最长公共子序列（LeetCode 1143：给两个字符串，求它们最长公共子序列的长度，子序列不要求连续）**是同一框架的另一个变体：字符相同时 &lt;code&gt;dp[i][j] = dp[i-1][j-1] + 1&lt;/code&gt;，不同时取 &lt;code&gt;max(dp[i-1][j], dp[i][j-1])&lt;/code&gt;——表的形状和编辑距离完全一样，只是&amp;quot;不匹配&amp;quot;时的转移规则不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最长递增子序列（LeetCode 300：给一个整数数组，求最长严格递增子序列的长度，子序列不要求连续）&lt;/strong&gt; 则是单序列的代表：&lt;code&gt;dp[i]&lt;/code&gt; 表示&amp;quot;以 &lt;code&gt;nums[i]&lt;/code&gt; 结尾&amp;quot;的最长递增子序列长度，状态转移是回头看所有比它小的元素：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lengthOfLIS&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; dp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;fill&lt;/span&gt;(dp, 1); &lt;span style=&#34;color:#75715e&#34;&gt;// 每个元素自己就是长度 1 的递增子序列&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; i; j&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;, dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(res, dp&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;书里还给了 O(n log n) 的二分优化，视角很妙：&lt;strong&gt;递增子序列问题等价于玩纸牌游戏&amp;quot;耐心排序&amp;quot;&lt;/strong&gt;——维护若干牌堆顶，每张牌放到第一个不小于它的堆顶，堆数就是 LIS 长度，堆顶数组天然单调递增所以可二分。一题多解，但起点都是那个 O(n^2) 的 dp 定义。&lt;/p&gt;
&lt;h3 id=&#34;25-系列三股票买卖状态机是-dp-的特例&#34;&gt;2.5 系列三：股票买卖——状态机是 DP 的特例&lt;/h3&gt;
&lt;p&gt;股票系列六题，书里用一个&lt;strong&gt;状态机&lt;/strong&gt;统一吃掉：状态是三元组&amp;quot;第几天、手里有没有股票、还能交易几次&amp;quot;，每天的选择只有买、卖、不动三种。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    state &amp;#34;手里无股票&amp;#34; as s0
    state &amp;#34;手里有股票&amp;#34; as s1
    [*] --&amp;gt; s0
    s0 --&amp;gt; s1 : 买入
    s1 --&amp;gt; s0 : 卖出
    s0 --&amp;gt; s0 : 不动
    s1 --&amp;gt; s1 : 不动&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;以**含冷冻期（LeetCode 309：给一个每日股价数组，可多次买卖但同一时刻最多持有一股，且卖出后第二天不能买入，求最大利润）**为例——只需让&amp;quot;买入&amp;quot;依赖前天的状态：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxProfit&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; prices) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; dp_i_0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;                  &lt;span style=&#34;color:#75715e&#34;&gt;// 今天结束时手里无股票的最大利润&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; dp_i_1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Integer.&lt;span style=&#34;color:#a6e22e&#34;&gt;MIN_VALUE&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// 今天结束时手里有股票的最大利润&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; dp_pre_0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;                &lt;span style=&#34;color:#75715e&#34;&gt;// dp[i-2][0]，前天的无股票利润（冷冻期用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; prices.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; temp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dp_i_0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dp_i_0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(dp_i_0, dp_i_1 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; prices&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;);    &lt;span style=&#34;color:#75715e&#34;&gt;// 昨天就无 / 今天卖出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dp_i_1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(dp_i_1, dp_pre_0 &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; prices&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;);  &lt;span style=&#34;color:#75715e&#34;&gt;// 昨天就有 / 前天无、冷冻后今天买入&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dp_pre_0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dp_i_0; &lt;span style=&#34;color:#75715e&#34;&gt;// 最后一天还持有股票没有意义，答案必在&amp;#34;无股票&amp;#34;状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;限交易次数就再加一维 &lt;code&gt;k&lt;/code&gt;，&amp;ldquo;买卖一次&amp;quot;是 &lt;code&gt;k = 1&lt;/code&gt;，&amp;ldquo;不限次数&amp;quot;是 &lt;code&gt;k&lt;/code&gt; 等于无穷大从而可以省略——&lt;strong&gt;六题全是同一套状态机的参数变化&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;26-系列四打家劫舍偷或不偷&#34;&gt;2.6 系列四：打家劫舍——偷或不偷&lt;/h3&gt;
&lt;p&gt;原题（LeetCode 198）：一排房屋，每间藏有一定现金，相邻两间不能同时偷（否则触发警报），求最多能偷多少。状态是&amp;quot;当前到第几间房&amp;rdquo;，选择是&amp;quot;偷或不偷&amp;rdquo;：偷了第 &lt;code&gt;i&lt;/code&gt; 间就不能偷第 &lt;code&gt;i - 1&lt;/code&gt; 间。环形版（LeetCode 213：房屋围成一圈，首尾不能同时偷）拆成&amp;quot;去掉首&amp;quot;和&amp;quot;去掉尾&amp;quot;两个普通版各跑一遍取最大；树上版（LeetCode 337：房屋被组织成一棵二叉树，直接相连的父子节点不能同时偷）把 dp 挂到二叉树的后序遍历上——又回到第 3 节的递归框架。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;书里那句大实话：&lt;strong&gt;状态转移方程不是想出来的，是把&amp;quot;状态、选择、base case&amp;quot;三步老老实实走完，它自己浮出水面的。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-二叉树所有递归的练兵场&#34;&gt;3. 二叉树：所有递归的练兵场&lt;/h2&gt;
&lt;p&gt;作者反复强调：&lt;strong&gt;递归的所有技巧都可以在二叉树上练，因为二叉树的遍历框架是最小、最干净的递归模板&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(TreeNode root) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 前序位置：进入节点时执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    traverse(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 中序位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    traverse(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 后序位置：离开节点时执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;框架里注释掉的那三个位置，是全书树题的题眼：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前序位置&lt;/strong&gt;的代码在&amp;quot;刚进入节点&amp;quot;时执行，此时只能利用父节点传下来的信息；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后序位置&lt;/strong&gt;的代码在&amp;quot;离开节点&amp;quot;时执行，此时已经拿到左右子树的计算结果——&lt;strong&gt;凡是要&amp;quot;汇总子树信息&amp;quot;的题（直径、最大路径和、判断是否平衡），答案一定在后序位置&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中序位置&lt;/strong&gt;在 BST 里等于升序遍历，BST 的题大多围绕这个性质。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个前序位置的最简例子是&lt;strong&gt;翻转二叉树（LeetCode 226：给一棵二叉树，交换每个节点的左右子树）&lt;/strong&gt;——交换只用当前节点自身的信息，天然属于前序位置：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TreeNode &lt;span style=&#34;color:#a6e22e&#34;&gt;invertTree&lt;/span&gt;(TreeNode root) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 前序位置：先交换当前节点的左右孩子，再递归下去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TreeNode temp &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; temp;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invertTree(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    invertTree(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; root;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;书里给递归定了一条纪律，专治&amp;quot;递归想不清楚&amp;quot;：&lt;strong&gt;写递归函数时，只定义&amp;quot;这个函数对当前节点做什么&amp;quot;，绝不在脑子里跳进递归里追细节&lt;/strong&gt;——递归树天然存在，你跳进一层就会跳进无数层。&lt;/p&gt;
&lt;h3 id=&#34;31-例题二叉树的直径leetcode-543后序位置的教科书用法&#34;&gt;3.1 例题：二叉树的直径（LeetCode 543）——后序位置的教科书用法&lt;/h3&gt;
&lt;p&gt;直径是任意两节点间最长路径的边数。关键观察：&lt;strong&gt;经过某个节点的最长路径 = 左子树深度 + 右子树深度&lt;/strong&gt;。要拿到左右子树的深度，代码必须放在后序位置：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; maxDiameter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;maxDepth&lt;/span&gt;(TreeNode root) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; maxDepth(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;);   &lt;span style=&#34;color:#75715e&#34;&gt;// 先递归拿到子树结果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; maxDepth(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 后序位置：此刻已掌握左右子树的全部信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    maxDiameter &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(maxDiameter, left &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; right);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; 1 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(left, right); &lt;span style=&#34;color:#75715e&#34;&gt;// 向父节点汇报自己的深度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意这个函数的&amp;quot;双重身份&amp;quot;：返回值是&amp;quot;深度&amp;quot;（给父节点用），全局变量维护的是&amp;quot;直径&amp;quot;（答案本身）。&lt;strong&gt;想清楚函数向上一级汇报什么，后序位置的题就通了&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;32-例题最近公共祖先leetcode-236&#34;&gt;3.2 例题：最近公共祖先（LeetCode 236）&lt;/h3&gt;
&lt;p&gt;给一棵二叉树和两个节点 p、q，求它们的最近公共祖先——即同时是两者祖先、且深度最大的那个节点：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TreeNode &lt;span style=&#34;color:#a6e22e&#34;&gt;lowestCommonAncestor&lt;/span&gt;(TreeNode root, TreeNode p, TreeNode q) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; p &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; q) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; root; &lt;span style=&#34;color:#75715e&#34;&gt;// base case：当前就是目标之一&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TreeNode left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lowestCommonAncestor(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;, p, q);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TreeNode right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lowestCommonAncestor(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;, p, q);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 后序位置：左右各找到一个，说明当前节点就是分叉点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (left &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; root;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; left : right;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;短短五行，能写对的前提是把函数定义想死：&amp;ldquo;返回以 root 为根的树里 p、q 的最近公共祖先，找不到就返回 null&amp;rdquo;，然后&lt;strong&gt;只处理当前节点这一层&lt;/strong&gt;，剩下的交给递归。&lt;/p&gt;
&lt;h3 id=&#34;33-bst把二分查找搬到树上&#34;&gt;3.3 BST：把二分查找搬到树上&lt;/h3&gt;
&lt;p&gt;BST 的中序遍历是升序序列，这一条性质撑起一族题。**第 K 小元素（LeetCode 230：给一棵 BST 和整数 k，求第 k 小的节点值）**就是中序遍历数到第 k 个：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;kthSmallest&lt;/span&gt;(TreeNode root, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    traverse(root, k);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;traverse&lt;/span&gt;(TreeNode root, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    traverse(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;, k);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    count&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;                    &lt;span style=&#34;color:#75715e&#34;&gt;// 中序位置：按升序被访问&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (count &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; k) { res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; root.&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    traverse(root.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;, k);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;BST 的增删查改则演示了另一个套路：&lt;strong&gt;利用&amp;quot;中序有序&amp;quot;的特性，每一步都是&amp;quot;和当前节点比大小，决定进左子树还是右子树&amp;quot;&lt;/strong&gt;——查（LeetCode 700）、插（LeetCode 701）、删（LeetCode 450）都是这个模式，本质是把二分查找的&amp;quot;区间折半&amp;quot;换成了&amp;quot;子树取舍&amp;quot;。删除节点时唯一麻烦的是目标节点有两个孩子：用右子树的最小节点（或左子树的最大节点）替位，保证 BST 性质不被破坏。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-回溯决策树的-dfs&#34;&gt;4. 回溯：决策树的 DFS&lt;/h2&gt;
&lt;p&gt;回溯算法在书里的定位非常干脆：&lt;strong&gt;回溯就是暴力穷举一棵决策树，外加撤销选择&lt;/strong&gt;。它和动态规划是亲兄弟——都是穷举，区别是回溯解决的问题没有重叠子问题（或者指数级的解空间本身就是你要的输出），所以备忘录帮不上忙，只能老老实实走完整棵树。&lt;/p&gt;
&lt;p&gt;框架就五行伪代码：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;result = []
void backtrack(路径, 选择列表) {
    if (满足结束条件) { result.add(路径); return; }
    for (选择 : 选择列表) {
        做选择（把选择加进路径）
        backtrack(路径, 更新后的选择列表)
        撤销选择（把选择从路径里删掉）
    }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个要素：&lt;strong&gt;路径&lt;/strong&gt;（已做的选择）、&lt;strong&gt;选择列表&lt;/strong&gt;（当前可做的选择）、&lt;strong&gt;结束条件&lt;/strong&gt;（到达决策树叶子）。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;全排列问题&amp;lt;br/&amp;gt;选择列表 1 2 3&amp;#34;] --&amp;gt; B[&amp;#34;选1&amp;lt;br/&amp;gt;列表剩 2 3&amp;#34;]
    A --&amp;gt; C[&amp;#34;选2&amp;lt;br/&amp;gt;列表剩 1 3&amp;#34;]
    B --&amp;gt; D[&amp;#34;选2 列表剩3&amp;lt;br/&amp;gt;到达叶子 收集结果&amp;#34;]
    D -.-&amp;gt;|&amp;#34;撤销选择&amp;lt;br/&amp;gt;回到父节点&amp;#34;| B
    B -.-&amp;gt;|&amp;#34;撤销选择&amp;#34;| A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;41-例题全排列leetcode-46框架的标准照&#34;&gt;4.1 例题：全排列（LeetCode 46）——框架的标准照&lt;/h3&gt;
&lt;p&gt;给一个不含重复数字的数组，返回它的所有可能排列。每层的选择列表是&amp;quot;还没用过的数字&amp;quot;，结束条件是路径长度等于 n：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; track &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(); &lt;span style=&#34;color:#75715e&#34;&gt;// 路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; used;                                  &lt;span style=&#34;color:#75715e&#34;&gt;// 标记哪些数字已进路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;permute&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    used &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    backtrack(nums);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;backtrack&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (track.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;) {  &lt;span style=&#34;color:#75715e&#34;&gt;// 结束条件：到达叶子&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(track));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (used&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// 已在路径里的跳过&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        track.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;);         &lt;span style=&#34;color:#75715e&#34;&gt;// 做选择&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        used&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        backtrack(nums);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        track.&lt;span style=&#34;color:#a6e22e&#34;&gt;removeLast&lt;/span&gt;();         &lt;span style=&#34;color:#75715e&#34;&gt;// 撤销选择：关键一步，漏了就错&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        used&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;42-例题子集leetcode-78结束条件藏在收集时机里&#34;&gt;4.2 例题：子集（LeetCode 78）——结束条件藏在收集时机里&lt;/h3&gt;
&lt;p&gt;给一个不含重复元素的数组，返回它的全部子集（幂集，共 2^n 个）。子集和排列的差别很小，却体现回溯的精髓：&lt;strong&gt;每个节点（不只叶子）都是一个子集，所以一进函数就先收集&lt;/strong&gt;；再用 &lt;code&gt;start&lt;/code&gt; 参数剪掉&amp;quot;回头路&amp;quot;，避免 &lt;code&gt;[1,2]&lt;/code&gt; 和 &lt;code&gt;[2,1]&lt;/code&gt; 算重：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; track &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subsets&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    backtrack(nums, 0);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;backtrack&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; start) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    res.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(track));   &lt;span style=&#34;color:#75715e&#34;&gt;// 前序位置收集：每个节点都是答案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; start; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        track.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        backtrack(nums, i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);         &lt;span style=&#34;color:#75715e&#34;&gt;// start 保证只往后选，组合不重复&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        track.&lt;span style=&#34;color:#a6e22e&#34;&gt;removeLast&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;组合（LeetCode 77：从 1 到 n 中选出 k 个数的所有组合）、组合总和（LeetCode 39：数组元素可重复选用，求所有凑成目标和的组合）全是这个模板的微调：改一下结束条件、允许不允许重复选同一个元素而已。&lt;/p&gt;
&lt;h3 id=&#34;43-例题n-皇后leetcode-51剪枝的价值&#34;&gt;4.3 例题：N 皇后（LeetCode 51）——剪枝的价值&lt;/h3&gt;
&lt;p&gt;在 n×n 的棋盘上放置 n 个皇后，要求任意两个不互相攻击（不同行、不同列、不同对角线），返回所有合法摆法。逐行放皇后是&amp;quot;做选择&amp;quot;，列和两条对角线的冲突检测是&amp;quot;剪枝&amp;quot;——&lt;strong&gt;回溯的效率全靠剪枝，剪掉一个不合法的分支就是砍掉一整棵子树&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArrayList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;solveNQueens&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[][]&lt;/span&gt; board &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;n&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; row : board) Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;fill&lt;/span&gt;(row, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    backtrack(board, 0);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;backtrack&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[][]&lt;/span&gt; board, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; row) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (row &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; board.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;) {      &lt;span style=&#34;color:#75715e&#34;&gt;// 结束条件：最后一行也放好了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; snapshot &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArrayList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; r : board) snapshot.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; String(r));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(snapshot);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; col &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; col &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; board.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; col&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;isValid(board, row, col)) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 剪枝：冲突直接跳过&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        board&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;row&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;col&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// 做选择&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        backtrack(board, row &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);  &lt;span style=&#34;color:#75715e&#34;&gt;// 逐行放，行内天然不冲突&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        board&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;row&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;col&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.&amp;#39;&lt;/span&gt;;      &lt;span style=&#34;color:#75715e&#34;&gt;// 撤销选择&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isValid&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[][]&lt;/span&gt; board, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; row, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; col) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 只需检查上方：同列、左上对角、右上对角（下面的行还没放）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; row; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (board&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;col&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1, j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; col &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; 0 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; 0; i&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;, j&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (board&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; row &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1, j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; col &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; 0 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; board.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;, j&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (board&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;][&lt;/span&gt;j&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Q&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&amp;ldquo;做选择 → 递归 → 撤销选择&amp;quot;这三板斧练熟，排列、组合、子集、括号生成（LeetCode 22：生成 n 对合法括号的所有组合）、解数独（LeetCode 37：填充 9×9 数独的空格，使每行、每列、每宫都合法），全是换皮。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-bfs最短路径的层序穷举&#34;&gt;5. BFS：最短路径的层序穷举&lt;/h2&gt;
&lt;p&gt;DFS 遍历到叶子才回头，BFS 则是一层一层往外扩——&lt;strong&gt;这个&amp;quot;齐头并进&amp;quot;的性质，决定了它的招牌场景：无权图的最短路径&lt;/strong&gt;。因为第 k 层就是&amp;quot;距离起点 k 步&amp;quot;的所有节点，第一次撞到终点时，步数必然最少。&lt;/p&gt;
&lt;p&gt;框架：队列 + visited 集合（防止走回头路死循环）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Queue q;  q.offer(起点);  visited.add(起点);
int step = 0;
while (q 非空) {
    把当前层的所有节点一次性取出，向四周扩散;
    未访问过的邻居入队并标记 visited;
    step++;
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;51-例题二叉树的最小深度leetcode-111&#34;&gt;5.1 例题：二叉树的最小深度（LeetCode 111）&lt;/h3&gt;
&lt;p&gt;给一棵二叉树，求从根节点到最近叶子节点的最短路径上的节点数。框架的&amp;quot;一次性取出当前层&amp;quot;用 &lt;code&gt;sz = q.size()&lt;/code&gt; 实现，这是 BFS 分层的关键手法：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;minDepth&lt;/span&gt;(TreeNode root) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (root &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Queue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;TreeNode&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; q &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(root);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; depth &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1; &lt;span style=&#34;color:#75715e&#34;&gt;// 起点算第 1 层&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;q.&lt;span style=&#34;color:#a6e22e&#34;&gt;isEmpty&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; sz &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; q.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;();          &lt;span style=&#34;color:#75715e&#34;&gt;// 锁定当前层的节点数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; sz; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            TreeNode cur &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; q.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; depth;       &lt;span style=&#34;color:#75715e&#34;&gt;// 第一个遇到的叶子必是最浅的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;left&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;right&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        depth&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; depth;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;为什么这题 BFS 优于 DFS？如果树很深但答案很浅（比如链式树、第二层就有叶子），DFS 会把最深的分支走完才回头，BFS 到第二层就收工。&lt;strong&gt;最短问题想 BFS，全部/存在性问题想 DFS&lt;/strong&gt;，大体不会错。&lt;/p&gt;
&lt;h3 id=&#34;52-例题打开转盘锁leetcode-752什么都能抽象成图&#34;&gt;5.2 例题：打开转盘锁（LeetCode 752）——什么都能抽象成图&lt;/h3&gt;
&lt;p&gt;从 &amp;ldquo;0000&amp;rdquo; 出发，每次只能把某一位向上或向下拨一格，不能经过 deadends 里的死亡组合，求到 target 的最少拨动次数。&lt;/p&gt;
&lt;p&gt;抽象的一步最见功力：&lt;strong&gt;每个四位组合是一个节点，拨一下是一条边，&amp;ldquo;最少几次&amp;quot;就是无权图最短路径&lt;/strong&gt;。每个节点固定有 8 个邻居（4 位 × 上下两个方向）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;openLock&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; deadends, String target) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Set&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; deads &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashSet&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;asList&lt;/span&gt;(deadends));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Set&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; visited &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashSet&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(); &lt;span style=&#34;color:#75715e&#34;&gt;// 走过的组合，防死循环&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Queue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; q &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; LinkedList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0000&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    visited.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0000&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; step &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;q.&lt;span style=&#34;color:#a6e22e&#34;&gt;isEmpty&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; sz &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; q.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; sz; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            String cur &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; q.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (deads.&lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;(cur)) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 死亡组合，掐断分支&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cur.&lt;span style=&#34;color:#a6e22e&#34;&gt;equals&lt;/span&gt;(target)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; step;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; j &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; 4; j&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {      &lt;span style=&#34;color:#75715e&#34;&gt;// 四个转盘&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                String up &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; plusOne(cur, j);   &lt;span style=&#34;color:#75715e&#34;&gt;// 第 j 位向上拨&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;visited.&lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;(up)) { visited.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(up); q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(up); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                String down &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; minusOne(cur, j); &lt;span style=&#34;color:#75715e&#34;&gt;// 第 j 位向下拨&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;visited.&lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;(down)) { visited.&lt;span style=&#34;color:#a6e22e&#34;&gt;add&lt;/span&gt;(down); q.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(down); }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        step&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;作者还提了&lt;strong&gt;双向 BFS&lt;/strong&gt;：从起点和终点同时扩散，两军会师即收兵，搜索空间从 &lt;code&gt;O(b^d)&lt;/code&gt; 降到 &lt;code&gt;O(b^(d/2))&lt;/code&gt;——但前提是终点明确。这种&amp;quot;优化只在特定前提成立时可用&amp;quot;的提醒，比优化本身更值钱。&lt;/p&gt;
&lt;p&gt;同构的练习题是&lt;strong&gt;单词接龙（LeetCode 127：给起始词、结束词和词典，每次只能改一个字母且中间词必须在词典里，求最短转换序列的长度）&lt;/strong&gt;——每个单词是节点、只差一个字母即连边，抽象完就是转盘锁换皮，双向 BFS 在此同样适用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-双指针滑动窗口与二分查找线性结构的剪刀&#34;&gt;6. 双指针、滑动窗口与二分查找：线性结构的剪刀&lt;/h2&gt;
&lt;p&gt;线性结构（数组、链表、字符串）上的套路，全书归纳成三组&amp;quot;剪刀&amp;rdquo;。&lt;/p&gt;
&lt;h3 id=&#34;61-快慢指针链表的三板斧&#34;&gt;6.1 快慢指针：链表的三板斧&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;例题一：删除链表的倒数第 N 个节点（LeetCode 19：给一个链表，删除倒数第 n 个节点并返回头节点）。&lt;/strong&gt; 快指针先走 n 步，然后快慢同步走；快指针停在末尾时，慢指针正好落在待删节点的前一个：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ListNode &lt;span style=&#34;color:#a6e22e&#34;&gt;removeNthFromEnd&lt;/span&gt;(ListNode head, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; n) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ListNode dummy &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ListNode(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1, head); &lt;span style=&#34;color:#75715e&#34;&gt;// 虚拟头节点，统一&amp;#34;删第一个&amp;#34;的情况&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ListNode fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dummy, slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dummy;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; n; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 快指针先走 n 步&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {   &lt;span style=&#34;color:#75715e&#34;&gt;// 快慢同步，快指针停在最后一个节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; slow.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    slow.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; slow.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;   &lt;span style=&#34;color:#75715e&#34;&gt;// 慢指针恰好是待删节点的前驱&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dummy.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;例题二：环形链表 II（LeetCode 142：给一个链表，判断是否有环，有则返回环的入口节点）。&lt;/strong&gt; 快指针走两步、慢指针走一步，能相遇则有环；相遇后让一个指针回到起点、两指针各走一步，再次相遇处就是环入口：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    H[&amp;#34;head&amp;#34;] --&amp;gt; A[&amp;#34;...&amp;#34;] --&amp;gt; C[&amp;#34;环入口&amp;#34;]
    C --&amp;gt; D[&amp;#34;...&amp;#34;] --&amp;gt; E[&amp;#34;第一次相遇点&amp;#34;]
    E --&amp;gt; F[&amp;#34;...&amp;#34;] --&amp;gt; C&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ListNode &lt;span style=&#34;color:#a6e22e&#34;&gt;detectCycle&lt;/span&gt;(ListNode head) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ListNode fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; head, slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; head;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (fast &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;   &lt;span style=&#34;color:#75715e&#34;&gt;// 快两步&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; slow.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;        &lt;span style=&#34;color:#75715e&#34;&gt;// 慢一步&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (fast &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; slow) &lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 相遇，证明有环&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (fast &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 走到头，无环&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; head;                 &lt;span style=&#34;color:#75715e&#34;&gt;// 慢指针回起点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (slow &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; fast) {       &lt;span style=&#34;color:#75715e&#34;&gt;// 各走一步再相遇，即环入口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        slow &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; slow.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fast &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fast.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; slow;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;为什么回起点后同步走能定位环入口？设起点到环入口 &lt;code&gt;k&lt;/code&gt; 步，相遇时慢指针走了 &lt;code&gt;s&lt;/code&gt; 步、快指针走了 &lt;code&gt;2s&lt;/code&gt; 步，多走的 &lt;code&gt;s&lt;/code&gt; 步必在环里绕了整数圈——所以相遇点再前进 &lt;code&gt;k&lt;/code&gt; 步就回到环入口，而起点前进 &lt;code&gt;k&lt;/code&gt; 步也到环入口，二者必在那里碰头。书里用这张图把这个&amp;quot;巧合&amp;quot;讲成了算术。&lt;/p&gt;
&lt;p&gt;同理，快指针到头时慢指针恰在&lt;strong&gt;链表中点&lt;/strong&gt;（LeetCode 876：返回链表的中间节点），中点又是归并排序链表、回文链表判断（LeetCode 234：判断链表是否为回文——先找中点、反转后半段、再逐位比对）的入口。&lt;/p&gt;
&lt;h3 id=&#34;62-左右指针从两头夹逼&#34;&gt;6.2 左右指针：从两头夹逼&lt;/h3&gt;
&lt;p&gt;从数组两端向中间走，每步都能排除一批不可能解——有序数组两数之和（LeetCode 167：在升序数组中找出和为 target 的两个数的下标）、反转数组、回文判断、二分查找都是它。以两数之和为例：&lt;code&gt;left&lt;/code&gt; 指小端、&lt;code&gt;right&lt;/code&gt; 指大端，和偏小就 &lt;code&gt;left++&lt;/code&gt;，偏大就 &lt;code&gt;right--&lt;/code&gt;，&lt;strong&gt;数组的有序性保证每次移动都不会错过答案&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;63-滑动窗口最小覆盖子串leetcode-76&#34;&gt;6.3 滑动窗口：最小覆盖子串（LeetCode 76）&lt;/h3&gt;
&lt;p&gt;本质是一个维护&amp;quot;窗口内某种状态&amp;quot;的左右指针：&lt;strong&gt;右指针负责扩张找可行解，左指针负责收缩找最优解&lt;/strong&gt;。书里把最小覆盖子串当模板题——在 &lt;code&gt;s&lt;/code&gt; 中找包含 &lt;code&gt;t&lt;/code&gt; 全部字符的最短子串：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;String &lt;span style=&#34;color:#a6e22e&#34;&gt;minWindow&lt;/span&gt;(String s, String t) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Map&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Character, Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; need &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashMap&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(), window &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashMap&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; c : t.&lt;span style=&#34;color:#a6e22e&#34;&gt;toCharArray&lt;/span&gt;()) need.&lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;(c, 1, Integer::sum);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, valid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;     &lt;span style=&#34;color:#75715e&#34;&gt;// valid：计数已满足的字符种类数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Integer.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAX_VALUE&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 最优解的起点和长度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (right &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; s.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(right);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        right&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;                            &lt;span style=&#34;color:#75715e&#34;&gt;// 右指针扩张窗口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (need.&lt;span style=&#34;color:#a6e22e&#34;&gt;containsKey&lt;/span&gt;(c)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            window.&lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;(c, 1, Integer::sum);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (window.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(c).&lt;span style=&#34;color:#a6e22e&#34;&gt;equals&lt;/span&gt;(need.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(c))) valid&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (valid &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; need.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;()) {      &lt;span style=&#34;color:#75715e&#34;&gt;// 窗口已覆盖 t，开始收缩&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (right &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; len) { start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left; len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; left; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(left);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            left&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (need.&lt;span style=&#34;color:#a6e22e&#34;&gt;containsKey&lt;/span&gt;(d)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (window.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(d).&lt;span style=&#34;color:#a6e22e&#34;&gt;equals&lt;/span&gt;(need.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(d))) valid&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                window.&lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;(d, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1, Integer::sum);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; len &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; Integer.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAX_VALUE&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; : s.&lt;span style=&#34;color:#a6e22e&#34;&gt;substring&lt;/span&gt;(start, start &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;need&lt;/code&gt; 和 &lt;code&gt;window&lt;/code&gt; 两张计数表就是窗口的&amp;quot;状态&amp;rdquo;，&lt;code&gt;valid&lt;/code&gt; 是&amp;quot;状态达标&amp;quot;的开关。这套写法一字不改地能套到&lt;strong&gt;无重复字符的最长子串（LeetCode 3：求不含重复字符的最长子串长度）&lt;/strong&gt;、&lt;strong&gt;字符串的排列（LeetCode 567：判断 s2 的子串中是否存在 s1 的某个排列）&lt;/strong&gt;、**找所有字母异位词（LeetCode 438：找出 s 中所有与 p 互为字母异位词的子串起点）**上，区别只在收缩条件和更新答案的时机。&lt;/p&gt;
&lt;h3 id=&#34;64-二分查找区间定义是一切的源头&#34;&gt;6.4 二分查找：区间定义是一切的源头&lt;/h3&gt;
&lt;p&gt;单独成章，因为&amp;quot;细节是魔鬼&amp;quot;：while 条件带不带等号、&lt;code&gt;mid&lt;/code&gt; 加一还是减一、&lt;code&gt;right&lt;/code&gt; 初值是 &lt;code&gt;n&lt;/code&gt; 还是 &lt;code&gt;n - 1&lt;/code&gt;，网上答案互相矛盾。作者的解法是&lt;strong&gt;先声明你维护的搜索区间&lt;/strong&gt;，其余全部推出，绝不靠记忆。以左闭右闭 &lt;code&gt;[left, right]&lt;/code&gt; 为例：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;binarySearch&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; target) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1; &lt;span style=&#34;color:#75715e&#34;&gt;// 区间 [left, right] 两端都可能命中&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; right) {                &lt;span style=&#34;color:#75715e&#34;&gt;// 区间为空（left == right + 1）才停&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (right &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; left) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; 2; &lt;span style=&#34;color:#75715e&#34;&gt;// 防溢出写法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;mid&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; target) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; mid;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;mid&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; target) left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1; &lt;span style=&#34;color:#75715e&#34;&gt;// 新区间 [mid+1, right]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1;                        &lt;span style=&#34;color:#75715e&#34;&gt;// 新区间 [left, mid-1]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;因为区间两端都&amp;quot;闭&amp;quot;，&lt;code&gt;mid&lt;/code&gt; 已经查过就必须排除出新区间（所以 &lt;code&gt;± 1&lt;/code&gt;），循环条件就是 &lt;code&gt;left &amp;lt;= right&lt;/code&gt;——&lt;strong&gt;每一行都能从区间定义推出来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;找左侧边界的变体（LeetCode 34：在含重复元素的升序数组中，找 target 第一次和最后一次出现的位置），只差&amp;quot;命中后不返回、继续向左收缩&amp;quot;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;leftBound&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; target) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; right) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (right &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; left) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; 2;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;mid&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; target) right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1; &lt;span style=&#34;color:#75715e&#34;&gt;// 锁定左侧，继续搜 [left, mid-1]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;mid&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; target) left &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mid &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; nums.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; nums&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;left&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; target) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; left;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;这三组剪刀的共同思想：&lt;strong&gt;利用有序性或单调性，让指针每走一步都排除掉一批不可能解&lt;/strong&gt;——这正是&amp;quot;聪明地穷举&amp;quot;在线性结构上的样子。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-其他套路速览每个都记一句什么时候用&#34;&gt;7. 其他套路速览：每个都记一句&amp;quot;什么时候用&amp;quot;&lt;/h2&gt;
&lt;p&gt;书的剩余部分是一批&amp;quot;小而锋利&amp;quot;的工具，先记一句话定位，再给两个最常用的补上关键代码：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前缀和&lt;/strong&gt;：需要反复查询&amp;quot;某段区间的和&amp;quot;时，先花 &lt;code&gt;O(n)&lt;/code&gt; 预处理，之后每次查询 &lt;code&gt;O(1)&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;差分数组&lt;/strong&gt;：需要对区间反复做&amp;quot;整体加减&amp;quot;时，只改区间的两个端点，最后一次性重建（LeetCode 370：区间加法；LeetCode 1094：拼车——上下车区间叠加后不能超过座位数）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单调栈&lt;/strong&gt;：解决&amp;quot;下一个更大元素&amp;quot;——栈里维护递减序列，新元素把比它小的全弹出去，被弹者的答案就是它；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并查集&lt;/strong&gt;：处理&amp;quot;连通性&amp;quot;问题，&lt;code&gt;union&lt;/code&gt; 合并、&lt;code&gt;find&lt;/code&gt; 查询，路径压缩后接近 &lt;code&gt;O(1)&lt;/code&gt;（LeetCode 200：数网格中岛屿数量——逐格合并相邻陆地，或直接 DFS/BFS&amp;quot;沉岛&amp;quot;计数）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LRU 缓存&lt;/strong&gt;：哈希表 + 双向链表——哈希表负责 &lt;code&gt;O(1)&lt;/code&gt; 查找，双向链表负责 &lt;code&gt;O(1)&lt;/code&gt; 维护访问顺序，满了从尾部淘汰（LeetCode 146：实现一个 get/put 都是 O(1) 的缓存）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;洗牌算法（Fisher-Yates）&lt;/strong&gt;：从后往前，每一位和它之前（含自己）的随机位交换，保证每种排列等概率（LeetCode 384：打乱数组）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;区间调度（贪心）&lt;/strong&gt;：安排最多互不冲突的区间，按结束时间排序、每步选结束最早的（LeetCode 435：最少删除多少区间使剩余互不重叠——正是此题的反面）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;71-例题每日温度leetcode-739单调栈&#34;&gt;7.1 例题：每日温度（LeetCode 739）——单调栈&lt;/h3&gt;
&lt;p&gt;给一个温度数组，对每一天回答&amp;quot;还要等几天才会更热&amp;quot;。倒序入栈，栈内维护单调递减：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dailyTemperatures&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;T.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Deque&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; stack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArrayDeque&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(); &lt;span style=&#34;color:#75715e&#34;&gt;// 存索引，对应温度单调递减&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; T.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; 0; i&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;isEmpty&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; T&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;peek&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; T&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;pop&lt;/span&gt;();              &lt;span style=&#34;color:#75715e&#34;&gt;// 弹出不如我热的：它们已被&amp;#34;我&amp;#34;挡住&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;isEmpty&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; 0 : stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;peek&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stack.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(i);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;每个元素最多入栈出栈各一次，O(n) 搞定 O(n^2) 的暴力。&lt;strong&gt;&amp;ldquo;下一个更大/更小元素&amp;quot;这类字眼一出现，条件反射想单调栈&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;72-例题和为-k-的子数组leetcode-560前缀和--哈希表&#34;&gt;7.2 例题：和为 K 的子数组（LeetCode 560）——前缀和 + 哈希表&lt;/h3&gt;
&lt;p&gt;朴素前缀和只能回答&amp;quot;从 0 到 i 的和&amp;rdquo;，配合哈希表记录每个前缀和的出现次数，就能 O(n) 数出所有和为 k 的连续子数组：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subarraySum&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Map&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer, Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; preSum &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashMap&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(); &lt;span style=&#34;color:#75715e&#34;&gt;// 前缀和 -&amp;gt; 出现次数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    preSum.&lt;span style=&#34;color:#a6e22e&#34;&gt;put&lt;/span&gt;(0, 1); &lt;span style=&#34;color:#75715e&#34;&gt;// 空前缀：和为 0 出现 1 次，处理从索引 0 开始的子数组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; sum &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, res &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; num : nums) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sum &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; num;                             &lt;span style=&#34;color:#75715e&#34;&gt;// 当前前缀和&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        res &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; preSum.&lt;span style=&#34;color:#a6e22e&#34;&gt;getOrDefault&lt;/span&gt;(sum &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; k, 0); &lt;span style=&#34;color:#75715e&#34;&gt;// 有几个前缀和等于 sum-k，就有几个合法子数组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        preSum.&lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;(sum, 1, Integer::sum);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;核心等式：若 &lt;code&gt;preSum[j] - preSum[i] = k&lt;/code&gt;，则 &lt;code&gt;(i, j]&lt;/code&gt; 是一段合法子数组。把&amp;quot;枚举所有区间&amp;quot;变成&amp;quot;查一次哈希表&amp;quot;，又是给穷举减肥的典型。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;8-带走的清单&#34;&gt;8. 带走的清单&lt;/h2&gt;
&lt;p&gt;把全书压成一张刷题前的检查清单：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拿到题先分类&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 求最值、问&amp;quot;最少/最多/最长&amp;quot;？十有八九是动态规划——走&amp;quot;状态 → dp 定义 → 选择 → base case&amp;quot;四步（参考凑零钱、背包、编辑距离）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 要列出&amp;quot;所有可行解&amp;quot;（排列、组合、棋盘）？回溯——写&amp;quot;路径、选择列表、结束条件&amp;quot;，再想剪枝（参考全排列、子集、N 皇后）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 无权图里问&amp;quot;最短/最少几步&amp;quot;？BFS——队列加 visited，层序扩散（参考转盘锁）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 线性结构上找子串、配对、倒数第 k 个？先想双指针和滑动窗口，别急着上重型武器（参考最小覆盖子串、倒数第 N 个节点）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 有序数组里查找？二分——先声明搜索区间的开闭，再写代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;写递归时的纪律&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 默念&amp;quot;一切递归皆树遍历&amp;quot;，画出那棵隐形的递归树。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 只定义当前节点做什么，不跳进递归追细节（参考最近公共祖先的五行代码）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 想清代码该放前序、中序还是后序位置：用父节点信息放前序，汇总子树结果放后序（参考二叉树直径）。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 递归树有大量重复子树？这就是重叠子问题，上备忘录或 DP 表（参考斐波那契）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;优化时的方向感&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 先写&amp;quot;暴力穷举但正确&amp;quot;的版本，再谈优化——备忘录、剪枝、指针单调性，全是给穷举减肥。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 反复查区间和 → 前缀和；反复改区间 → 差分；问&amp;quot;下一个更大&amp;quot; → 单调栈；问连通性 → 并查集。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 双向 BFS 这类优化有前提条件，用之前先确认前提成立。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;心态上&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 不背题，背框架；题目是无穷的，框架是有限的。&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 刷题量在框架之后：框架不熟时刷 300 题，不如框架熟了刷 100 题。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这本书的最大价值不是哪个具体算法，而是它把刷题从&amp;quot;背答案的体力活&amp;quot;变成了&amp;quot;识别穷举结构的智力活&amp;quot;——看透每个技巧都只是在给穷举减肥，算法题就从玄学变回了工程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;参考书籍：labuladong（付东来）《labuladong 的算法小抄》，电子工业出版社（博文视点），2020；配套开源项目见其 GitHub 算法教程仓库。文中代码按书中框架用 Java 整理，对应 LeetCode 题号已标注。&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
