<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>子集 on Jeanphilo Blog</title><link>https://jeanphilogong.github.io/jeanblog/zh/tags/%E5%AD%90%E9%9B%86/</link><description>Recent content in 子集 on Jeanphilo Blog</description><generator>Hugo -- 0.168.0</generator><language>zh-cn</language><lastBuildDate>Sun, 03 May 2026 14:25:07 +0800</lastBuildDate><atom:link href="https://jeanphilogong.github.io/jeanblog/zh/tags/%E5%AD%90%E9%9B%86/index.xml" rel="self" type="application/rss+xml"/><item><title>LeetCode 78：子集，从搜索树推出 startIndex 回溯模板</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/78-subsets/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/78-subsets/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：&lt;code&gt;nums&lt;/code&gt;，长度 &lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;元素范围：&lt;code&gt;-10 &amp;lt;= nums[i] &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;所有元素互不相同&lt;/li&gt;
&lt;li&gt;输出：返回 &lt;code&gt;nums&lt;/code&gt; 的所有可能子集&lt;/li&gt;
&lt;li&gt;顺序：结果顺序不限，子集内部不需要考虑排列顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python 构造一个最小可运行解法。&lt;/p&gt;
&lt;h2 id="从-12-的搜索树开始"&gt;从 &lt;code&gt;[1,2]&lt;/code&gt; 的搜索树开始&lt;/h2&gt;
&lt;p&gt;最小但有分叉的例子是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案应该是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[], [1], [1,2], [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里没有 &lt;code&gt;[2,1]&lt;/code&gt;。这说明问题不是“随便排列所有选择”，而是“每个元素选或不选，且同一组元素只出现一次”。&lt;/p&gt;
&lt;p&gt;如果把构造过程画成树：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- [1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- [1,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这棵树暴露出两个事实：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个节点都是一个合法子集。&lt;/li&gt;
&lt;li&gt;进入 &lt;code&gt;[1]&lt;/code&gt; 之后，后面只能继续看 &lt;code&gt;2&lt;/code&gt;，不能回头再选 &lt;code&gt;1&lt;/code&gt; 或生成 &lt;code&gt;[2,1]&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1先说清楚一个递归层在解决什么"&gt;Step 1：先说清楚一个递归层在解决什么&lt;/h2&gt;
&lt;p&gt;如果当前已经选出一个 &lt;code&gt;path&lt;/code&gt;，剩下的问题是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从某个起点之后的元素里，继续选择若干个元素，扩展当前 &lt;code&gt;path&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个“某个起点”必须被记下来。否则 &lt;code&gt;[1,2]&lt;/code&gt; 和 &lt;code&gt;[2,1]&lt;/code&gt; 会从不同路径重复出现。&lt;/p&gt;
&lt;p&gt;先写最小骨架。它只保留两个状态：&lt;/p&gt;</description></item><item><title>LeetCode 90：子集 II，从重复分支推出层内去重</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/90-subsets-ii/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/90-subsets-ii/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：&lt;code&gt;nums&lt;/code&gt;，长度 &lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;元素范围：&lt;code&gt;-10 &amp;lt;= nums[i] &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums&lt;/code&gt; 可能有重复元素&lt;/li&gt;
&lt;li&gt;输出：返回所有不重复子集&lt;/li&gt;
&lt;li&gt;顺序：结果顺序不限，但同一个值序列的子集只能出现一次&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：[[],[1],[1,2],[1,2,2],[2],[2,2]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从一个能跑但浪费的版本，一步一步过渡到排序 + 层内去重。&lt;/p&gt;
&lt;h2 id="从-122-的重复分支开始"&gt;从 &lt;code&gt;[1,2,2]&lt;/code&gt; 的重复分支开始&lt;/h2&gt;
&lt;p&gt;最小能暴露问题的例子是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2,2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果直接照搬 &lt;code&gt;78. 子集&lt;/code&gt; 的模板，搜索树里会出现两条不同分支：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;选择下标 1 的 2 -&amp;gt; [2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;选择下标 2 的 2 -&amp;gt; [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这两个分支路径不同，但值序列相同，最终答案重复。&lt;/p&gt;
&lt;p&gt;所以这题真正新增的问题不是“会不会回溯”，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;怎样跳过重复分支，同时保留 &lt;code&gt;[2,2]&lt;/code&gt; 这种合法答案？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1先复用-78-的状态看看哪里会坏"&gt;Step 1：先复用 78 的状态，看看哪里会坏&lt;/h2&gt;
&lt;p&gt;当前部分答案仍然需要 &lt;code&gt;path&lt;/code&gt;；当前层仍然需要 &lt;code&gt;start&lt;/code&gt; 控制只能往右选。&lt;/p&gt;
&lt;p&gt;先写出 78 的核心版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dfs&lt;/span&gt;(start: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;copy())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(start, len(nums)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(nums[i])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dfs(i &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个版本在 &lt;code&gt;nums&lt;/code&gt; 全部互不相同时是正确的。&lt;/p&gt;</description></item></channel></rss>