<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>笺札</title>
  
  
  <link href="https://blog.id-zyuan.xyz/atom.xml" rel="self"/>
  
  <link href="https://blog.id-zyuan.xyz/"/>
  <updated>2026-10-12T15:26:41.950Z</updated>
  <id>https://blog.id-zyuan.xyz/</id>
  
  <author>
    <name>zhaoyuan</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>1141. 查询近30天活跃用户数</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/12/%E6%9F%A5%E8%AF%A2%E8%BF%9130%E5%A4%A9%E6%B4%BB%E8%B7%83%E7%94%A8%E6%88%B7%E6%95%B0/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/12/%E6%9F%A5%E8%AF%A2%E8%BF%9130%E5%A4%A9%E6%B4%BB%E8%B7%83%E7%94%A8%E6%88%B7%E6%95%B0/</id>
    <published>2026-10-11T16:00:00.000Z</published>
    <updated>2026-10-12T15:26:41.950Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1141-查询近30天活跃用户数"><a class="markdownIt-Anchor" href="#1141-查询近30天活跃用户数"></a> 1141. 查询近30天活跃用户数</h1><blockquote><p><strong>难度</strong>：简单 | <strong>题号</strong>：1141 | <strong>链接</strong>：<a href="https://leetcode.cn/problems/user-activity-for-the-past-30-days-i/">1141. 查询近30天活跃用户数 - 力扣（LeetCode）</a> | <strong>数据结构</strong>：SQL</p><p>关联：订单最多的用户（同为「按维度分组计数」）· 每月交易 I（同为日期函数 + 分组聚合）· 可对照 SQL原理_02（日期函数 / 分组聚合）。</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>描述</strong>：写一条 SQL 查询，求出<strong>截至 2019-07-27（含）的近 30 天</strong>内，<strong>每天的活跃用户数</strong>。活跃用户指<strong>当天至少进行一次活动</strong>的用户，返回结果表的列名须为 <code>day</code> 和 <code>active_users</code>，且<strong>每天都可能出现重复用户</strong>（同一用户一天多次活动只算一次）。</p><p><strong>表：Activity</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+---------------+---------+</span><br><span class="line">| Column Name   | Type    |</span><br><span class="line">+---------------+---------+</span><br><span class="line">| user_id       | int     |</span><br><span class="line">| session_id    | int     |</span><br><span class="line">| activity_date | date    |</span><br><span class="line">| activity_type | enum    |</span><br><span class="line">+---------------+---------+</span><br><span class="line">该表没有主键，它可能有重复行。</span><br><span class="line">activity_type 列是 ENUM [&#x27;open_session&#x27;, &#x27;end_session&#x27;, &#x27;scroll_down&#x27;, &#x27;send_message&#x27;] 之一。</span><br><span class="line">该表包含用户 ID、会话 ID、活动日期与活动类型。</span><br></pre></td></tr></table></figure><p><strong>示例 1</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">Activity table:</span><br><span class="line">+---------+------------+---------------+---------------+</span><br><span class="line">| user_id | session_id | activity_date | activity_type |</span><br><span class="line">+---------+------------+---------------+---------------+</span><br><span class="line">| 1       | 1          | 2019-07-20    | open_session  |</span><br><span class="line">| 1       | 1          | 2019-07-20    | scroll_down   |</span><br><span class="line">| 1       | 1          | 2019-07-20    | end_session   |</span><br><span class="line">| 2       | 4          | 2019-07-20    | open_session  |</span><br><span class="line">| 2       | 4          | 2019-07-21    | send_message  |</span><br><span class="line">| 2       | 4          | 2019-07-21    | end_session   |</span><br><span class="line">| 3       | 2          | 2019-07-21    | open_session  |</span><br><span class="line">| 3       | 2          | 2019-07-21    | send_message  |</span><br><span class="line">| 3       | 2          | 2019-07-21    | end_session   |</span><br><span class="line">| 4       | 3          | 2019-06-25    | open_session  |</span><br><span class="line">| 4       | 3          | 2019-06-25    | end_session   |</span><br><span class="line">+---------+------------+---------------+---------------+</span><br><span class="line">输出：</span><br><span class="line">+------------+--------------+</span><br><span class="line">| day        | active_users |</span><br><span class="line">+------------+--------------+</span><br><span class="line">| 2019-07-20 | 2            |</span><br><span class="line">| 2019-07-21 | 2            |</span><br><span class="line">+------------+--------------+</span><br><span class="line">解释：</span><br><span class="line">- 2019-06-25 不在近 30 天窗口内（早于 2019-06-28），被排除。</span><br><span class="line">- 2019-07-20 有用户 1、2 活动 → 2 个活跃用户。</span><br><span class="line">- 2019-07-21 有用户 2、3 活动 → 2 个活跃用户。</span><br></pre></td></tr></table></figure><p><strong>提示</strong>：</p><ul><li><strong>窗口是「左闭右开」的滑窗</strong>：<code>activity_date</code> 落在 <code>[2019-06-28, 2019-07-27]</code> 之间，即 <code>DATEDIFF('2019-07-27', activity_date) &lt; 30</code>（等价于 <code>&lt;= 29</code>）。</li><li><strong>同一天同一用户多次活动只能计一次</strong> → 必须 <code>COUNT(DISTINCT user_id)</code>。</li><li>结果按天分组，列名严格为 <code>day</code> / <code>active_users</code>（<code>active_users</code> 是<strong>保留歧义最小</strong>的别名，建议加反引号或双引号）。</li></ul><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1where-筛日期-按天分组去重计数用户解法"><a class="markdownIt-Anchor" href="#思路1where-筛日期-按天分组去重计数用户解法"></a> 思路1：WHERE 筛日期 + 按天分组去重计数（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>三步走：① <code>WHERE</code> 用 <code>DATEDIFF</code> 把数据裁到近 30 天窗口；② <code>GROUP BY activity_date</code> 按天分组；③ <code>COUNT(DISTINCT user_id)</code> 数出当天不重复的活跃用户数，并把日期列取别名为 <code>day</code>。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># Write your MySQL query statement below</span><br><span class="line"><span class="keyword">SELECT</span> activity_date <span class="keyword">AS</span> <span class="keyword">day</span>,</span><br><span class="line">       <span class="built_in">COUNT</span>(<span class="keyword">DISTINCT</span> user_id) <span class="keyword">AS</span> active_users</span><br><span class="line"><span class="keyword">FROM</span> Activity</span><br><span class="line"><span class="keyword">WHERE</span> activity_date <span class="operator">&lt;=</span> <span class="string">&#x27;2019-07-27&#x27;</span></span><br><span class="line">  <span class="keyword">AND</span> DATEDIFF(<span class="string">&#x27;2019-07-27&#x27;</span>, activity_date) <span class="operator">&lt;</span> <span class="number">30</span></span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> activity_date;</span><br></pre></td></tr></table></figure><blockquote><p>💡 精简写法：<code>BETWEEN '2019-06-28' AND '2019-07-27'</code> 更直观（右边界含 27 号，左边界 28 号正好 30 天）；用 <code>DATEDIFF</code> 的好处是无需手算起始日期。</p></blockquote><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（扫描全表 + 分组聚合；<code>activity_date</code> 上有索引时可先 range scan 裁剪数据量）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mclose">)</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal">d</span></span></span></span> 为不同日期数（分组哈希表规模）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li><strong>别用 <code>&gt;= 30</code></strong>：<code>DATEDIFF('2019-07-27', '2019-06-27') = 30</code>，已经出界；<code>&lt; 30</code> 或 <code>&lt;= 29</code> 才是「近 30 天」。</li><li>本题是<strong>滑动窗口计数</strong>的模板：<em>先用日期函数把窗口裁出来，再分组去重计数</em>。同类场景：日活 / 周活 / 月活（DAU/WAU/MAU）。</li><li>若题目改成「近 30 天<strong>总体</strong>活跃用户数」，就要去掉 <code>GROUP BY</code>，直接 <code>COUNT(DISTINCT user_id)</code>。</li></ul><h2 id="相关链接"><a class="markdownIt-Anchor" href="#相关链接"></a> 🔗 相关链接</h2><ul><li>所属 MOC：学习-数据结构与算法MOC</li><li>题集索引：LeetCode题集索引</li><li>关联概念：SQL原理_02（日期函数 / 分组聚合）</li><li>同类题目：每月交易 I · 订单最多的用户</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p>「<strong>滑动窗口 + 按维度去重计数</strong>」模板：</p><ul><li>用 <code>DATEDIFF(ref_date, date_col) &lt; N</code> 或 <code>BETWEEN ... AND ...</code> 裁出窗口 → <code>GROUP BY 日期</code> → <code>COUNT(DISTINCT 用户)</code>。</li><li>两个易错点：① 窗口边界左闭右开（<code>&lt; 30</code> 不是 <code>&lt;= 30</code>）；② 同一用户同一天多次活动必须 <code>DISTINCT</code>。</li></ul><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC<br />📎 同类题：每月交易 I · 订单最多的用户 ｜ 概念：SQL原理_02</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>1193. 每月交易 I</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/12/%E6%AF%8F%E6%9C%88%E4%BA%A4%E6%98%93%20I/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/12/%E6%AF%8F%E6%9C%88%E4%BA%A4%E6%98%93%20I/</id>
    <published>2026-10-11T16:00:00.000Z</published>
    <updated>2026-10-12T15:26:41.952Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1193-每月交易-i"><a class="markdownIt-Anchor" href="#1193-每月交易-i"></a> 1193. 每月交易 I</h1><blockquote><p><strong>难度</strong>：中等 | <strong>题号</strong>：1193 | <strong>链接</strong>：<a href="https://leetcode.cn/problems/monthly-transactions-i/">1193. 每月交易 I - 力扣（LeetCode）</a> | <strong>数据结构</strong>：SQL</p><p>关联：查询近30天活跃用户数（同为日期函数 + 分组聚合）· 订单最多的用户（分组计数）· 可对照 SQL原理_02（日期格式化 / 条件聚合）。</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>描述</strong>：写一条 SQL 查询，找出<strong>每个月、每个国家/地区</strong>的：① 交易总数；② 交易总金额；③ <strong>已批准</strong>（<code>state = 'approved'</code>）的交易数；④ <strong>已批准</strong>交易的总额。结果以<strong>任意顺序</strong>返回。</p><p><strong>表：Transactions</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+---------------+---------+</span><br><span class="line">| Column Name   | Type    |</span><br><span class="line">+---------------+---------+</span><br><span class="line">| id            | int     |</span><br><span class="line">| country       | varchar |</span><br><span class="line">| state         | enum    |</span><br><span class="line">| amount        | int     |</span><br><span class="line">| trans_date    | date    |</span><br><span class="line">+---------------+---------+</span><br><span class="line">id 是该表的主键（具有唯一值的列）。</span><br><span class="line">state 列类型为 ENUM [&#x27;approved&#x27;, &#x27;declined&#x27;] 之一。</span><br><span class="line">该表包含有关传入交易的信息。</span><br></pre></td></tr></table></figure><p><strong>示例 1</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">Transactions table:</span><br><span class="line">+------+---------+----------+--------+------------+</span><br><span class="line">| id   | country | state    | amount | trans_date |</span><br><span class="line">+------+---------+----------+--------+------------+</span><br><span class="line">| 121  | US      | approved | 1000   | 2018-12-18 |</span><br><span class="line">| 122  | US      | declined | 2000   | 2018-12-19 |</span><br><span class="line">| 123  | US      | approved | 2000   | 2019-01-01 |</span><br><span class="line">| 124  | DE      | approved | 2000   | 2019-01-07 |</span><br><span class="line">+------+---------+----------+--------+------------+</span><br><span class="line">输出：</span><br><span class="line">+----------+---------+-------------+----------------+--------------------+-----------------------+</span><br><span class="line">| month    | country | trans_count | approved_count | trans_total_amount | approved_total_amount |</span><br><span class="line">+----------+---------+-------------+----------------+--------------------+-----------------------+</span><br><span class="line">| 2018-12  | US      | 2           | 1              | 3000               | 1000                  |</span><br><span class="line">| 2019-01  | US      | 1           | 1              | 2000               | 2000                  |</span><br><span class="line">| 2019-01  | DE      | 1           | 1              | 2000               | 2000                  |</span><br><span class="line">+----------+---------+-------------+----------------+--------------------+-----------------------+</span><br></pre></td></tr></table></figure><p><strong>提示</strong>：</p><ul><li><strong>先按月 + 国家分组</strong>，再在组内同时计算「总计数 / 条件计数 / 总金额 / 条件金额」——典型<strong>条件聚合（Conditional Aggregation）</strong>。</li><li>月份要从 <code>trans_date</code> 抽出来：<code>DATE_FORMAT(trans_date, '%Y-%m')</code>（或 <code>SUBSTRING(trans_date, 1, 7)</code>）。</li><li>条件计数有两种写法：<code>SUM(state = 'approved')</code> 直接对布尔求和（MySQL 中真值 = 1）；或 <code>COUNT(IF(state = 'approved', 1, NULL))</code>（只数非 NULL，更通用）。</li><li>结果列名必须与要求完全一致。</li></ul><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1date_format-条件聚合用户解法"><a class="markdownIt-Anchor" href="#思路1date_format-条件聚合用户解法"></a> 思路1：DATE_FORMAT + 条件聚合（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>用 <code>DATE_FORMAT(trans_date, '%Y-%m')</code> 造出月份列并按 <code>(month, country)</code> 分组；组内分别用 <code>COUNT(*)</code>、<code>SUM(布尔)</code>、<code>SUM(amount)</code>、<code>SUM(IF(...))</code> 算出四个指标。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># Write your MySQL query statement below</span><br><span class="line"><span class="keyword">SELECT</span> DATE_FORMAT(trans_date, <span class="string">&#x27;%Y-%m&#x27;</span>) <span class="keyword">AS</span> <span class="keyword">month</span>,</span><br><span class="line">       country,</span><br><span class="line">       <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">AS</span> trans_count,</span><br><span class="line">       <span class="built_in">COUNT</span>(IF(state <span class="operator">=</span> <span class="string">&#x27;approved&#x27;</span>, <span class="number">1</span>, <span class="keyword">NULL</span>)) <span class="keyword">AS</span> approved_count,</span><br><span class="line">       <span class="built_in">SUM</span>(amount) <span class="keyword">AS</span> trans_total_amount,</span><br><span class="line">       <span class="built_in">SUM</span>(IF(state <span class="operator">=</span> <span class="string">&#x27;approved&#x27;</span>, amount, <span class="number">0</span>)) <span class="keyword">AS</span> approved_total_amount</span><br><span class="line"><span class="keyword">FROM</span> Transactions</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="keyword">month</span>, country;</span><br></pre></td></tr></table></figure><blockquote><p>💡 更简洁的等价写法（利用 MySQL 布尔转 0/1）：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> DATE_FORMAT(trans_date, <span class="string">&#x27;%Y-%m&#x27;</span>) <span class="keyword">AS</span> <span class="keyword">month</span>, country,</span><br><span class="line">       <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">AS</span> trans_count,</span><br><span class="line">       <span class="built_in">SUM</span>(state <span class="operator">=</span> <span class="string">&#x27;approved&#x27;</span>) <span class="keyword">AS</span> approved_count,</span><br><span class="line">       <span class="built_in">SUM</span>(amount) <span class="keyword">AS</span> trans_total_amount,</span><br><span class="line">       <span class="built_in">SUM</span>(IF(state <span class="operator">=</span> <span class="string">&#x27;approved&#x27;</span>, amount, <span class="number">0</span>)) <span class="keyword">AS</span> approved_total_amount</span><br><span class="line"><span class="keyword">FROM</span> Transactions</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="number">1</span>, <span class="number">2</span>;</span><br></pre></td></tr></table></figure></blockquote><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（全表扫描 + 哈希分组聚合；<code>(trans_date, country)</code> 有索引时可加速）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(g)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mclose">)</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi></mrow><annotation encoding="application/x-tex">g</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span></span></span></span> 为 <code>(月, 国家)</code> 组合数。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li><strong><code>GROUP BY month</code> 合法吗？</strong> MySQL 允许按 <code>SELECT</code> 里的<strong>别名</strong>分组（<code>GROUP BY month</code> / <code>GROUP BY 1</code>），但<strong>标准 SQL 不支持</strong>，跨库迁移时建议写完整的 <code>DATE_FORMAT(...)</code>。</li><li><strong><code>COUNT(IF(...))</code> vs <code>SUM(IF(...))</code></strong>：计数用 <code>COUNT(IF(cond, 1, NULL))</code>（<code>COUNT</code> 忽略 NULL）；求和用 <code>SUM(IF(cond, amount, 0))</code>（补 0 避免 NULL 污染、也避免少加行）。用 <code>SUM(state = 'approved')</code> 最省，但依赖 MySQL 的布尔→整数转换。</li><li><strong>NULL 陷阱</strong>：<code>SUM</code> 遇到全 NULL 组会返回 <code>NULL</code> 而非 0，所以条件求和务必显式补 <code>0</code>。</li><li>进阶：LeetCode 1194「每月交易 II」在此基础上要额外处理**退款（chargeback）**与原交易的配平，会用到自连接 / 窗口函数。</li></ul><h2 id="相关链接"><a class="markdownIt-Anchor" href="#相关链接"></a> 🔗 相关链接</h2><ul><li>所属 MOC：学习-数据结构与算法MOC</li><li>题集索引：LeetCode题集索引</li><li>关联概念：SQL原理_02（日期函数 / 条件聚合 / NULL 处理）</li><li>同类题目：查询近30天活跃用户数（窗口 + 分组）· 订单最多的用户（分组计数）</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p>「<strong>条件聚合（Pivot 式多指标聚合）</strong>」模板：</p><ul><li>先派生时间维度列（<code>DATE_FORMAT</code> / <code>SUBSTRING</code>）→ 按维度 <code>GROUP BY</code> → 组内用 <code>COUNT / COUNT(IF()) / SUM / SUM(IF())</code> 一次算出多个指标。</li><li>三个关键点：① 月份抽取用日期函数，别手写 <code>SUBSTRING</code> 更稳；② 条件计数用 <code>COUNT(IF(cond,1,NULL))</code>、条件求和用 <code>SUM(IF(cond,val,0))</code>；③ <code>SUM</code> 对全 NULL 组返回 NULL，补 <code>0</code> 最安全。</li></ul><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC<br />📎 同类题：查询近30天活跃用户数 · 订单最多的用户 ｜ 概念：SQL原理_02</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>1341. 电影评分</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/12/%E7%94%B5%E5%BD%B1%E8%AF%84%E5%88%86/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/12/%E7%94%B5%E5%BD%B1%E8%AF%84%E5%88%86/</id>
    <published>2026-10-11T16:00:00.000Z</published>
    <updated>2026-10-12T15:26:41.953Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1341-电影评分"><a class="markdownIt-Anchor" href="#1341-电影评分"></a> 1341. 电影评分</h1><blockquote><p><strong>难度</strong>：中等 | <strong>题号</strong>：1341 | <strong>链接</strong>：<a href="https://leetcode.cn/problems/movie-rating/">1341. 电影评分 - 力扣（LeetCode）</a> | <strong>数据结构</strong>：SQL</p><p>关联：订单最多的用户（同为「分组计数取 Top-1」）· 分数排名（同为「排名 / 并列处理」）· 部门工资最高的员工（分组最值）· 可对照 SQL原理_02（多表连接 / 排序 / <code>UNION ALL</code>）。</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>描述</strong>：写一个解决方案，同时完成两件事：</p><ol><li>找出<strong>评论电影数量最多</strong>的用户名。若并列，返回<strong>字典序较小</strong>的用户名。</li><li>找出 <strong>2020 年 2 月</strong>里<strong>平均评分最高</strong>的电影名称。若并列，返回<strong>字典序较小</strong>的电影名称。</li></ol><p>返回结果表的列名为 <code>results</code>。</p><p><strong>表：Movies</strong> / <strong>表：Users</strong> / <strong>表：MovieRating</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">+---------------+---------+      +---------------+---------+      +---------------+---------+</span><br><span class="line">| Column Name   | Type    |      | Column Name   | Type    |      | Column Name   | Type    |</span><br><span class="line">+---------------+---------+      +---------------+---------+      +---------------+---------+</span><br><span class="line">| movie_id      | int     |      | user_id       | int     |      | movie_id      | int     |</span><br><span class="line">| title         | varchar |      | name          | varchar |      | user_id       | int     |</span><br><span class="line">+---------------+---------+      +---------------+---------+      | rating        | int     |</span><br><span class="line">movie_id 是主键。                user_id 是主键。                 | created_at    | date    |</span><br><span class="line">title 唯一。                     name 唯一。                      +---------------+---------+</span><br><span class="line">                                                                 (movie_id, user_id) 是联合主键。</span><br><span class="line">                                                                 created_at 为点评日期。</span><br></pre></td></tr></table></figure><p><strong>示例 1</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">Movies 表：              Users 表：          MovieRating 表：</span><br><span class="line">+----------+----------+  +--------+-------+  +----------+---------+--------+------------+</span><br><span class="line">| movie_id | title    |  | user_id| name  |  | movie_id | user_id | rating | created_at |</span><br><span class="line">+----------+----------+  +--------+-------+  +----------+---------+--------+------------+</span><br><span class="line">| 1        | Avengers |  | 1      | Daniel|  | 1        | 1       | 3      | 2020-01-12 |</span><br><span class="line">| 2        | Frozen 2 |  | 2      | Monica|  | 1        | 2       | 4      | 2020-02-11 |</span><br><span class="line">| 3        | Joker    |  | 3      | Maria |  | 1        | 3       | 2      | 2020-02-12 |</span><br><span class="line">+----------+----------+  | 4      | James |  | 1        | 4       | 1      | 2020-01-01 |</span><br><span class="line">                         +--------+-------+  | 2        | 1       | 5      | 2020-02-17 |</span><br><span class="line">                                             | 2        | 2       | 2      | 2020-02-01 |</span><br><span class="line">                                             | 2        | 3       | 2      | 2020-03-01 |</span><br><span class="line">                                             | 3        | 1       | 3      | 2020-02-22 |</span><br><span class="line">                                             | 3        | 2       | 4      | 2020-02-25 |</span><br><span class="line">                                             +----------+---------+--------+------------+</span><br><span class="line">输出：</span><br><span class="line">+----------+</span><br><span class="line">| results  |</span><br><span class="line">+----------+</span><br><span class="line">| Daniel   |</span><br><span class="line">| Frozen 2 |</span><br><span class="line">+----------+</span><br><span class="line">解释：</span><br><span class="line">- Daniel 和 Monica 都点评了 3 部电影，但 Daniel 字典序较小 → 取 Daniel。</span><br><span class="line">- Frozen 2 与 Joker 在 2 月的平均评分都是 3.5，但 Frozen 2 字典序较小 → 取 Frozen 2。</span><br></pre></td></tr></table></figure><p><strong>提示</strong>：</p><ul><li>两问结果是<strong>两条独立的行</strong>，需要把两个查询的结果<strong>竖着拼</strong>起来 → 用 <code>UNION ALL</code>（不要用 <code>UNION</code>，语义上不清、还可能误去重）。</li><li>「并列取字典序较小」= <code>ORDER BY 主指标 DESC, 名字 ASC</code> 后 <code>LIMIT 1</code>，一次排序同时解决并列。</li><li>第二问要先 <code>WHERE created_at</code> 限定在 <code>2020-02-01 ~ 2020-02-29</code>，再按电影分组取平均分。</li><li>每问都要 <code>LIMIT 1</code> —— <strong>子查询里各自排序取一行</strong>，外层再 <code>UNION ALL</code>。</li></ul><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1两个子查询各取-top-1再-union-all用户解法"><a class="markdownIt-Anchor" href="#思路1两个子查询各取-top-1再-union-all用户解法"></a> 思路1：两个子查询各取 Top-1，再 UNION ALL（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>把题目拆成两个互不相干的「Top-1」：① 用户名按「评论电影数」降序、名字升序 → 第一行；② 电影名按「2 月平均评分」降序、名字升序 → 第一行。两者列名都对齐成 <code>results</code>，用 <code>UNION ALL</code> 竖拼。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"># Write your MySQL query statement below</span><br><span class="line">(</span><br><span class="line">    <span class="keyword">SELECT</span> u.name <span class="keyword">AS</span> results</span><br><span class="line">    <span class="keyword">FROM</span> Users u</span><br><span class="line">    <span class="keyword">JOIN</span> MovieRating mr <span class="keyword">ON</span> u.user_id <span class="operator">=</span> mr.user_id</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> u.user_id</span><br><span class="line">    <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="built_in">COUNT</span>(<span class="keyword">DISTINCT</span> mr.movie_id) <span class="keyword">DESC</span>, u.name <span class="keyword">ASC</span></span><br><span class="line">    LIMIT <span class="number">1</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">UNION</span> <span class="keyword">ALL</span></span><br><span class="line">(</span><br><span class="line">    <span class="keyword">SELECT</span> m.title <span class="keyword">AS</span> results</span><br><span class="line">    <span class="keyword">FROM</span> Movies m</span><br><span class="line">    <span class="keyword">JOIN</span> MovieRating mr <span class="keyword">ON</span> m.movie_id <span class="operator">=</span> mr.movie_id</span><br><span class="line">    <span class="keyword">WHERE</span> mr.created_at <span class="operator">&gt;=</span> <span class="string">&#x27;2020-02-01&#x27;</span></span><br><span class="line">      <span class="keyword">AND</span> mr.created_at <span class="operator">&lt;</span> <span class="string">&#x27;2020-03-01&#x27;</span></span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> m.movie_id</span><br><span class="line">    <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="built_in">AVG</span>(mr.rating) <span class="keyword">DESC</span>, m.title <span class="keyword">ASC</span></span><br><span class="line">    LIMIT <span class="number">1</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（两次分组聚合 + 两次排序；排序是主导项）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（分组哈希表 / 排序临时表）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li><strong><code>COUNT(DISTINCT movie_id)</code> 的必要性</strong>：<code>(movie_id, user_id)</code> 是联合主键，理论上一个用户对同一部电影只有一条记录，<code>COUNT(*)</code> 也可行；但写 <code>COUNT(DISTINCT movie_id)</code> 语义更清晰（「评论了多少部<strong>电影</strong>」而非「多少条记录」），防御性更强。</li><li><strong><code>ORDER BY 主指标 DESC, 名字 ASC</code></strong> 是处理「并列取字典序最小」的通用技巧——把并列打平逻辑交给排序，而不是写在 <code>WHERE</code> 里。</li><li><strong>日期边界用左闭右开</strong>：<code>&gt;= '2020-02-01' AND &lt; '2020-03-01'</code> 比 <code>BETWEEN ... AND '2020-02-29'</code> 更稳（闰年 / 不同月天数都不会错）。</li><li><strong>括号包裹子查询 + <code>LIMIT</code></strong>：MySQL 里 <code>UNION ALL</code> 两侧的 <code>ORDER BY ... LIMIT</code> 必须用括号包起来，否则会被当成整体排序；若需要最终结果保持两个子查询的顺序，可在外层再套一层排序或依赖 MySQL 实现（题解一般允许）。</li><li><strong><code>UNION</code> vs <code>UNION ALL</code></strong>：本题两行<strong>理论上可能同名</strong>（某用户名恰好等于某电影名），用 <code>UNION</code> 会被误去重 → 必须 <code>UNION ALL</code>。</li></ul><h3 id="思路2用窗口函数替代-limit可扩展写法"><a class="markdownIt-Anchor" href="#思路2用窗口函数替代-limit可扩展写法"></a> 思路2：用窗口函数替代 LIMIT（可扩展写法）</h3><h4 id="核心逻辑-2"><a class="markdownIt-Anchor" href="#核心逻辑-2"></a> 核心逻辑</h4><p>如果题目改成「两个指标都要输出多个并列结果」或需要在同一查询里做更多排名，可把 <code>ORDER BY ... LIMIT 1</code> 换成 <code>RANK() OVER (ORDER BY 指标 DESC, 名字 ASC)</code> 再筛 <code>rk = 1</code>。本题只需一行，<code>LIMIT 1</code> 已足够，此处仅作对 分数排名 一类题的写法呼应。</p><h4 id="代码实现-2"><a class="markdownIt-Anchor" href="#代码实现-2"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> name <span class="keyword">AS</span> results</span><br><span class="line"><span class="keyword">FROM</span> (</span><br><span class="line">    <span class="keyword">SELECT</span> u.name, <span class="built_in">RANK</span>() <span class="keyword">OVER</span> (<span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="built_in">COUNT</span>(<span class="keyword">DISTINCT</span> mr.movie_id) <span class="keyword">DESC</span>, u.name <span class="keyword">ASC</span>) <span class="keyword">AS</span> rk</span><br><span class="line">    <span class="keyword">FROM</span> Users u <span class="keyword">JOIN</span> MovieRating mr <span class="keyword">ON</span> u.user_id <span class="operator">=</span> mr.user_id</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> u.user_id</span><br><span class="line">) t</span><br><span class="line"><span class="keyword">WHERE</span> rk <span class="operator">=</span> <span class="number">1</span>;</span><br></pre></td></tr></table></figure><h4 id="思考-2"><a class="markdownIt-Anchor" href="#思考-2"></a> 思考</h4><ul><li>用了 <code>RANK</code> 而非 <code>DENSE_RANK</code>：题目要求「并列取一个」，靠名字二次排序已保证唯一，其实 <code>ROW_NUMBER()</code> 也够；<code>RANK</code> 语义上更贴近「第一名」。</li><li>两套思路本质相同——<strong>先算指标，再排序取第一名</strong>；区别只是「筛选第一名」用 <code>LIMIT</code> 还是 <code>rk = 1</code>。</li></ul><h2 id="相关链接"><a class="markdownIt-Anchor" href="#相关链接"></a> 🔗 相关链接</h2><ul><li>所属 MOC：学习-数据结构与算法MOC</li><li>题集索引：LeetCode题集索引</li><li>关联概念：SQL原理_02（多表连接 / 排序 / UNION）· SQL原理_03（窗口函数）</li><li>同类题目：订单最多的用户 · 分数排名 · 部门工资最高的员工</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p>「<strong>多个独立 Top-1 拼装</strong>」模板：</p><ul><li>每问单独写「分组 → 排序（主指标 DESC, 次要键用于打平）→ <code>LIMIT 1</code>」，用括号包住 <code>ORDER BY ... LIMIT</code>，再用 <code>UNION ALL</code> 竖拼。</li><li>三个关键点：① 「并列取字典序最小」交给她 <code>ORDER BY 主指标 DESC, 名字 ASC</code>；② 日期边界用左闭右开；③ 竖拼两个同类结果用 <code>UNION ALL</code>（可能同名，别用 <code>UNION</code>）。</li></ul><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC<br />📎 同类题：订单最多的用户 · 分数排名 · 部门工资最高的员工 ｜ 概念：SQL原理_02 · SQL原理_03</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>596. 超过5名学生的课</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/12/%E8%B6%85%E8%BF%87%E4%BA%94%E5%90%8D%E5%AD%A6%E7%94%9F%E7%9A%84%E8%AF%BE/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/12/%E8%B6%85%E8%BF%87%E4%BA%94%E5%90%8D%E5%AD%A6%E7%94%9F%E7%9A%84%E8%AF%BE/</id>
    <published>2026-10-11T16:00:00.000Z</published>
    <updated>2026-10-12T15:26:41.953Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="596-超过5名学生的课"><a class="markdownIt-Anchor" href="#596-超过5名学生的课"></a> 596. 超过5名学生的课</h1><blockquote><p><strong>难度</strong>：简单 | <strong>题号</strong>：596 | <strong>链接</strong>：<a href="https://leetcode.cn/problems/classes-more-than-5-students/">596. 超过 5 名学生的课 - 力扣（LeetCode）</a> | <strong>数据结构</strong>：SQL</p><p>关联：订单最多的用户（同为「分组计数 + 过滤」）· 查询近30天活跃用户数（分组去重计数）· 可对照 SQL原理_02（<code>GROUP BY</code> / <code>HAVING</code> / <code>WHERE</code> 与 <code>HAVING</code> 的区别）。</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>描述</strong>：查询<strong>至少有 5 名学生</strong>的所有课程（班级）。结果以<strong>任意顺序</strong>返回。</p><p><strong>表：Courses</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">+-------------+---------+</span><br><span class="line">| Column Name | Type    |</span><br><span class="line">+-------------+---------+</span><br><span class="line">| student     | varchar |</span><br><span class="line">| class       | varchar |</span><br><span class="line">+-------------+---------+</span><br><span class="line">(student, class) 是该表的联合主键（不同值的列的组合）。</span><br><span class="line">该表的每一行表示学生的名字和他们注册的课程。</span><br></pre></td></tr></table></figure><p><strong>示例 1</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">Courses table:</span><br><span class="line">+---------+----------+</span><br><span class="line">| student | class    |</span><br><span class="line">+---------+----------+</span><br><span class="line">| A       | Math     |</span><br><span class="line">| B       | English  |</span><br><span class="line">| C       | Math     |</span><br><span class="line">| D       | Biology  |</span><br><span class="line">| E       | Math     |</span><br><span class="line">| F       | Computer |</span><br><span class="line">| G       | Math     |</span><br><span class="line">| H       | Math     |</span><br><span class="line">| I       | Math     |</span><br><span class="line">+---------+----------+</span><br><span class="line">输出：</span><br><span class="line">+---------+</span><br><span class="line">| class   |</span><br><span class="line">+---------+</span><br><span class="line">| Math    |</span><br><span class="line">+---------+</span><br><span class="line">解释：</span><br><span class="line">- Math 课有 6 名学生，包含它。</span><br><span class="line">- English / Biology / Computer 各只有 1 名学生，不包含。</span><br></pre></td></tr></table></figure><p><strong>提示</strong>：</p><ul><li><strong>先按 <code>class</code> 分组计数，再对分组结果过滤</strong> → 必须用 <code>HAVING</code>，不能用 <code>WHERE</code>（<code>WHERE</code> 在分组<strong>之前</strong>过滤行，看不到 <code>COUNT</code>）。</li><li>题目提示「同一学生在同一课程中不应被重复计算」→ 本表 <code>(student, class)</code> 是联合主键天然去重，<code>COUNT(*)</code> 即可；但写 <code>COUNT(DISTINCT student)</code> <strong>更防御</strong>（防止数据重复时误算）。</li><li><code>HAVING</code> 中<strong>不能引用列别名</strong>（<code>HAVING cnt &gt;= 5</code> 会报 <code>Unknown column</code>），要写完整聚合表达式 <code>COUNT(*) &gt;= 5</code>。</li></ul><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1group-by-having用户解法"><a class="markdownIt-Anchor" href="#思路1group-by-having用户解法"></a> 思路1：GROUP BY + HAVING（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>按 <code>class</code> 分组，<code>COUNT(*)</code> 数出每门课的学生数，<code>HAVING COUNT(*) &gt;= 5</code> 筛出人数达标的课程。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># Write your MySQL query statement below</span><br><span class="line"><span class="keyword">SELECT</span> class</span><br><span class="line"><span class="keyword">FROM</span> Courses</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> class</span><br><span class="line"><span class="keyword">HAVING</span> <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="operator">&gt;=</span> <span class="number">5</span>;</span><br></pre></td></tr></table></figure><blockquote><p>💡 更防御的写法（同一学生重复选课时也正确）：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> class</span><br><span class="line"><span class="keyword">FROM</span> Courses</span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> class</span><br><span class="line"><span class="keyword">HAVING</span> <span class="built_in">COUNT</span>(<span class="keyword">DISTINCT</span> student) <span class="operator">&gt;=</span> <span class="number">5</span>;</span><br></pre></td></tr></table></figure></blockquote><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（全表扫描 + 哈希分组；<code>class</code> 上有索引可加速分组）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>c</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(c)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">c</span><span class="mclose">)</span></span></span></span>，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow><annotation encoding="application/x-tex">c</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">c</span></span></span></span> 为课程数。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li><strong><code>WHERE</code> vs <code>HAVING</code></strong> —— 本题最核心的考点：<ul><li><code>WHERE</code> 作用于<strong>原始行</strong>，在 <code>GROUP BY</code> <strong>之前</strong>执行，用不了聚合函数；</li><li><code>HAVING</code> 作用于<strong>分组后的结果</strong>，在 <code>GROUP BY</code> <strong>之后</strong>执行，专门用来筛选聚合值。</li><li>口诀：<strong>「行级条件走 WHERE，组级条件走 HAVING」</strong>。</li></ul></li><li><strong>别把 <code>student_count</code> 当别名用在 <code>HAVING</code> 里</strong>：<code>SELECT class, COUNT(*) AS cnt ... HAVING cnt &gt;= 5</code> 在 MySQL 里会报 <code>Error 1054: Unknown column 'cnt' in 'having clause'</code>（<code>HAVING</code> 的求值早于 <code>SELECT</code> 别名解析）→ 必须重写 <code>COUNT(*)</code>。</li><li><strong><code>COUNT(*)</code> 还是 <code>COUNT(DISTINCT student)</code></strong>：依赖联合主键去重时二者等价；若表可能存在重复插入了同一 <code>(student, class)</code>，只有 <code>DISTINCT</code> 版才正确。工程里<strong>宁可多写 <code>DISTINCT</code></strong>。</li></ul><h2 id="相关链接"><a class="markdownIt-Anchor" href="#相关链接"></a> 🔗 相关链接</h2><ul><li>所属 MOC：学习-数据结构与算法MOC</li><li>题集索引：LeetCode题集索引</li><li>关联概念：SQL原理_02（分组过滤 / WHERE 与 HAVING）</li><li>同类题目：订单最多的用户（HAVING 计数）· 查询近30天活跃用户数（<code>COUNT(DISTINCT ...)</code>）</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p>「<strong>分组后按计数过滤</strong>」模板：</p><ul><li><code>GROUP BY 分组列 HAVING COUNT(...) &gt;= N</code>，一句话即得。</li><li>三个关键点：① 组级条件用 <code>HAVING</code>，别用 <code>WHERE</code>；② <code>HAVING</code> 里不能引别名，写全 <code>COUNT(*)</code>；③ 人数类统计优先 <code>COUNT(DISTINCT 学生)</code> 防重复数据。</li></ul><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC<br />📎 同类题：订单最多的用户 · 查询近30天活跃用户数 ｜ 概念：SQL原理_02</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>586. 订单最多的用户</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/12/%E8%AE%A2%E5%8D%95%E6%9C%80%E5%A4%9A%E7%9A%84%E7%94%A8%E6%88%B7/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/12/%E8%AE%A2%E5%8D%95%E6%9C%80%E5%A4%9A%E7%9A%84%E7%94%A8%E6%88%B7/</id>
    <published>2026-10-11T16:00:00.000Z</published>
    <updated>2026-10-12T14:40:46.404Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="586-订单最多的用户"><a class="markdownIt-Anchor" href="#586-订单最多的用户"></a> 586. 订单最多的用户</h1><blockquote><p><strong>难度</strong>：简单 | <strong>题号</strong>：586 | <strong>链接</strong>：<a href="https://leetcode.cn/problems/customer-placing-the-largest-number-of-orders/description/">586. 订单最多的客户 - 力扣（LeetCode）</a> | <strong>数据结构</strong>：SQL</p><p>关联：从不订购的用户（同用 Orders 表，一文求&quot;从未下单&quot;，一文求&quot;下单最多&quot;）· 分数排名（同为「分组计数 + 排名」题）· 可对照 SQL原理_03（CTE 写法）与 SQL原理_02（窗口函数）。</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>描述</strong>：查找<strong>订单数量最多</strong>的客户的 <code>customer_number</code>。测试用例保证订单最多的客户<strong>只有一个</strong>。</p><p><strong>表：Orders</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">+-----------------+----------+</span><br><span class="line">| Column Name     | Type     |</span><br><span class="line">+-----------------+----------+</span><br><span class="line">| order_number    | int      |</span><br><span class="line">| customer_number | int      |</span><br><span class="line">+-----------------+----------+</span><br><span class="line">order_number 是该表的主键（具有唯一值的列）。</span><br><span class="line">该表包含订单 ID 与下单客户 ID 的对应关系。</span><br></pre></td></tr></table></figure><p><strong>示例 1</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">输入：</span><br><span class="line">Orders 表：</span><br><span class="line">+--------------+-----------------+</span><br><span class="line">| order_number | customer_number |</span><br><span class="line">+--------------+-----------------+</span><br><span class="line">| 1            | 1               |</span><br><span class="line">| 2            | 2               |</span><br><span class="line">| 3            | 3               |</span><br><span class="line">| 4            | 3               |</span><br><span class="line">+--------------+-----------------+</span><br><span class="line">输出：</span><br><span class="line">+-----------------+</span><br><span class="line">| customer_number |</span><br><span class="line">+-----------------+</span><br><span class="line">| 3               |</span><br><span class="line">+-----------------+</span><br><span class="line">解释：</span><br><span class="line">customer_number = 3 的客户共有 2 个订单，多于只有 1 个订单的客户 1 和客户 2。</span><br></pre></td></tr></table></figure><p><strong>提示</strong>：</p><ul><li>先「按客户分组计数」，再「取计数最大的那一组」——本质是<strong>分组 Top-1</strong>。</li><li>题目只保证「唯一」，但<strong>追问</strong>要求能处理<strong>并列第一</strong>的情况（见思路 2）。</li><li>返回结果只有一列 <code>customer_number</code>。</li></ul><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1group-by-order-by-limit-1用户解法"><a class="markdownIt-Anchor" href="#思路1group-by-order-by-limit-1用户解法"></a> 思路1：GROUP BY + ORDER BY … LIMIT 1（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>第一步按 <code>customer_number</code> 分组，<code>COUNT(*)</code> 数出每人订单数；第二步按订单数降序排，<code>LIMIT 1</code> 取第一名。用 CTE（<code>WITH</code>）把这两步拆开，可读性更好。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># Write your MySQL query statement below</span><br><span class="line"><span class="keyword">WITH</span> t1 <span class="keyword">AS</span> (</span><br><span class="line">    <span class="keyword">SELECT</span> customer_number, <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">AS</span> n</span><br><span class="line">    <span class="keyword">FROM</span> Orders</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> customer_number</span><br><span class="line">)</span><br><span class="line"><span class="keyword">SELECT</span> customer_number</span><br><span class="line"><span class="keyword">FROM</span> t1</span><br><span class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> n <span class="keyword">DESC</span></span><br><span class="line">LIMIT <span class="number">1</span>;</span><br></pre></td></tr></table></figure><blockquote><p>也可以省掉 CTE 一步写完：<code>SELECT customer_number FROM Orders GROUP BY customer_number ORDER BY COUNT(*) DESC LIMIT 1;</code></p></blockquote><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（<code>GROUP BY</code> 聚合后对结果排序，排序是主导项；走主键索引可直接扫描计数）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（分组结果的哈希/临时表）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li><strong><code>LIMIT 1</code> 的并列陷阱</strong>：如果多个客户并列最多，<code>LIMIT 1</code> 只返回其中一个，且返回哪一个<strong>不确定</strong>（取决于排序稳定性）。本题靠「唯一」保证兜底才安全。</li><li>评论区常见报错：<strong>聚合函数不能嵌套</strong>，<code>MAX(COUNT(*))</code> 是非法的 → 要么先聚合再排序取首，要么把计数做成子查询再取 <code>MAX</code>。</li></ul><h3 id="思路2窗口函数-dense_rank应对并列-follow-up"><a class="markdownIt-Anchor" href="#思路2窗口函数-dense_rank应对并列-follow-up"></a> 思路2：窗口函数 DENSE_RANK（应对并列 · Follow-up）</h3><h4 id="核心逻辑-2"><a class="markdownIt-Anchor" href="#核心逻辑-2"></a> 核心逻辑</h4><p>对每个客户的订单数排名，取 <code>ranking = 1</code> 的全部客户。并列第一时这里会<strong>全部保留</strong>，正好回答题目的追问「如果订单最多的客户有多个怎么办」。</p><h4 id="代码实现-2"><a class="markdownIt-Anchor" href="#代码实现-2"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> customer_number</span><br><span class="line"><span class="keyword">FROM</span> (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">        customer_number,</span><br><span class="line">        <span class="built_in">DENSE_RANK</span>() <span class="keyword">OVER</span> (<span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">DESC</span>) <span class="keyword">AS</span> ranking</span><br><span class="line">    <span class="keyword">FROM</span> Orders</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> customer_number</span><br><span class="line">) <span class="keyword">AS</span> t</span><br><span class="line"><span class="keyword">WHERE</span> ranking <span class="operator">=</span> <span class="number">1</span>;</span><br></pre></td></tr></table></figure><h4 id="性能分析-2"><a class="markdownIt-Anchor" href="#性能分析-2"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（分组 + 窗口排序）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>。</li><li>与思路 1 同阶，但<strong>语义更强</strong>（天然处理并列），代价是写法更重。</li></ul><h4 id="思考-2"><a class="markdownIt-Anchor" href="#思考-2"></a> 思考</h4><ul><li><code>RANK()</code> / <code>DENSE_RANK()</code> 在并列时都会给出 <code>1</code>；差别在<strong>后续名次</strong>（<code>RANK</code> 跳号、<code>DENSE_RANK</code> 不跳）。本题只取第一名，二者等价；若像 分数排名 那样需要完整名次，就必须想清楚要哪一种。</li><li>另一类等价写法用 <code>HAVING COUNT(*) = (SELECT MAX(cnt) FROM …)</code>，靠子查询取最大计数——可读但重复聚合一次，不如窗口函数干净。</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p>「<strong>每组计数的 Top-1</strong>」模板：</p><ul><li><strong>只需第一名</strong> → <code>GROUP BY 分组列</code> + <code>ORDER BY COUNT(*) DESC</code> + <code>LIMIT 1</code>（可包一层 CTE 提升可读性）；</li><li><strong>需处理并列 / 完整名次</strong> → 子查询里 <code>DENSE_RANK() OVER (ORDER BY COUNT(*) DESC)</code>，外层筛 <code>ranking = 1</code>。</li></ul><p>核心坑点：① 聚合函数不能嵌套（<code>MAX(COUNT(*))</code> 非法）；② <code>LIMIT 1</code> 在并列时结果不确定，仅适合作题目的「唯一」保证。</p><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC<br />📎 同类题：从不订购的用户 · 分数排名 · 部门工资最高的员工 ｜ 概念：SQL原理_02 · SQL原理_03</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>漏斗题</title>
    <link href="https://blog.id-zyuan.xyz/2026/10/03/%E6%BC%8F%E6%96%97%E9%A2%98/"/>
    <id>https://blog.id-zyuan.xyz/2026/10/03/%E6%BC%8F%E6%96%97%E9%A2%98/</id>
    <published>2026-10-02T16:00:00.000Z</published>
    <updated>2026-10-12T14:40:46.388Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="漏斗题"><a class="markdownIt-Anchor" href="#漏斗题"></a> 漏斗题</h1><blockquote><p><strong>类型</strong>：自编场景题（漏斗转化）| <strong>题号</strong>：— | <strong>链接</strong>：—</p><p>关联：SQL原理_04（漏斗章节）· SQL分析场景模板 · 好友申请 II ：谁有最多的好友（同为列转行+聚合）</p></blockquote><h2 id="问题描述"><a class="markdownIt-Anchor" href="#问题描述"></a> 🎯 问题描述</h2><p><strong>表结构</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">event_log(user_id, event_name, event_time)</span><br></pre></td></tr></table></figure><p>事件名：<code>view</code>、<code>cart</code>、<code>order</code>、<code>pay</code>。</p><p><strong>需求</strong></p><p>统计从&quot;浏览 → 加购 → 下单 → 支付&quot;的漏斗人数。</p><p>要求每一步必须发生在第一步之后，即按时间顺序：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">view_time &lt; cart_time &lt; order_time &lt; pay_time</span><br></pre></td></tr></table></figure><p>输出：<code>view_users</code>、<code>cart_users</code>、<code>order_users</code>、<code>pay_users</code>。</p><p><strong>说明</strong>：每个用户可能多次触发同一事件，需按<strong>最早时间</strong>归并成一行再比较顺序。</p><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路1mincase-when-归并-时间递增计数用户解法"><a class="markdownIt-Anchor" href="#思路1mincase-when-归并-时间递增计数用户解法"></a> 思路1：MIN(CASE WHEN) 归并 + 时间递增计数（用户解法）</h3><h4 id="核心逻辑"><a class="markdownIt-Anchor" href="#核心逻辑"></a> 核心逻辑</h4><p>① 子查询 <code>person_log</code>：按 <code>user_id</code> 分组，用 <code>MIN(CASE WHEN event_name = 'xx' THEN event_time END)</code> 把每个事件的<strong>最早时间</strong>抽成一列（列转行 → 转回列）；<br />② 外层按时间顺序<strong>逐级比较</strong>：走到下一步的前提是上一步时间更早，用 <code>COUNT(CASE WHEN 条件 THEN 1 END)</code> 数各步人数；<br />③ 缺 <code>ELSE</code> 时 <code>CASE</code> 返回 <code>NULL</code>，<code>COUNT</code> 只数非空，因此只统计&quot;真的依次走到该步&quot;的用户。</p><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">WITH</span> person_log <span class="keyword">AS</span> (</span><br><span class="line">    <span class="keyword">SELECT</span></span><br><span class="line">        user_id,</span><br><span class="line">        <span class="built_in">MIN</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> event_name <span class="operator">=</span> <span class="string">&#x27;view&#x27;</span>  <span class="keyword">THEN</span> event_time <span class="keyword">END</span>) <span class="keyword">AS</span> u_view,</span><br><span class="line">        <span class="built_in">MIN</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> event_name <span class="operator">=</span> <span class="string">&#x27;cart&#x27;</span>  <span class="keyword">THEN</span> event_time <span class="keyword">END</span>) <span class="keyword">AS</span> u_cart,</span><br><span class="line">        <span class="built_in">MIN</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> event_name <span class="operator">=</span> <span class="string">&#x27;order&#x27;</span> <span class="keyword">THEN</span> event_time <span class="keyword">END</span>) <span class="keyword">AS</span> u_order,</span><br><span class="line">        <span class="built_in">MIN</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> event_name <span class="operator">=</span> <span class="string">&#x27;pay&#x27;</span>   <span class="keyword">THEN</span> event_time <span class="keyword">END</span>) <span class="keyword">AS</span> u_pay</span><br><span class="line">    <span class="keyword">FROM</span> event_log</span><br><span class="line">    <span class="keyword">GROUP</span> <span class="keyword">BY</span> user_id</span><br><span class="line">)</span><br><span class="line"><span class="keyword">SELECT</span></span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> u_view <span class="keyword">IS</span> <span class="keyword">NOT NULL</span> <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">END</span>) <span class="keyword">AS</span> view_users,</span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> u_view <span class="operator">&lt;</span> u_cart <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">END</span>) <span class="keyword">AS</span> cart_users,</span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> u_view <span class="operator">&lt;</span> u_cart <span class="keyword">AND</span> u_cart <span class="operator">&lt;</span> u_order <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">END</span>) <span class="keyword">AS</span> order_users,</span><br><span class="line">    <span class="built_in">COUNT</span>(<span class="keyword">CASE</span> <span class="keyword">WHEN</span> u_view <span class="operator">&lt;</span> u_cart <span class="keyword">AND</span> u_cart <span class="operator">&lt;</span> u_order <span class="keyword">AND</span> u_order <span class="operator">&lt;</span> u_pay <span class="keyword">THEN</span> <span class="number">1</span> <span class="keyword">END</span>) <span class="keyword">AS</span> pay_users</span><br><span class="line"><span class="keyword">FROM</span> person_log;</span><br></pre></td></tr></table></figure><blockquote><p>🔧 <strong>修正说明</strong>：</p><ol><li>原文 <code>u_cartTHEN</code> 缺空格，已改为 <code>u_cart THEN</code>。</li><li>原文第一步写 <code>COUNT(CASE WHEN u_view THEN 1 END)</code> —— 直接拿时间做布尔判断，遇到 <code>'0000-00-00'</code> 这类假值会误判；应显式写 <strong><code>u_view IS NOT NULL</code></strong> 才准确。</li><li>时间比较也可以用 <code>DATEDIFF(u_cart, u_view) &gt; 0</code>，语义等价。</li></ol></blockquote><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>（一次分组聚合即可，无 JOIN）。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>（相对输入）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><ul><li>这是<strong>严格有序漏斗</strong>：<code>view &lt; cart &lt; order &lt; pay</code> 全部成立才算走完。若业务允许&quot;乱序转化&quot;（比如直接下单没加购），条件要相应放宽。</li><li>若要算<strong>转化率</strong>，再除以 <code>view_users</code> 即可：<code>cart_users / view_users</code> 等。</li><li>若同一用户同事件多天发生、且要求&quot;同一会话内&quot;，需要额外按会话/时间窗切分。</li></ul><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 📝 总结</h2><p><strong>漏斗模板</strong>：<code>MIN(CASE WHEN 事件 THEN 时间 END)</code> 把各步压成一行 → <strong>按时间先后逐级比较</strong> → <code>COUNT(CASE WHEN ... THEN 1 END)</code> 数各步人数（无 <code>ELSE</code> 即天然只数&quot;真走到&quot;的）。与 SQL原理_04 漏斗一节完全同源。</p><hr /><blockquote><p>🔗 题集索引：LeetCode题集索引 · 算法地图：学习-数据结构与算法MOC</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="题目" scheme="https://blog.id-zyuan.xyz/categories/%E9%A2%98%E7%9B%AE/"/>
    
    
    <category term="算法" scheme="https://blog.id-zyuan.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="SQL" scheme="https://blog.id-zyuan.xyz/tags/SQL/"/>
    
  </entry>
  
  <entry>
    <title>删除有序数组中的重复项 -LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/10/%E5%88%A0%E9%99%A4%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E9%A1%B9/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/10/%E5%88%A0%E9%99%A4%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E9%A1%B9/</id>
    <published>2026-07-09T16:00:00.000Z</published>
    <updated>2026-07-10T12:06:19.796Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="26-删除有序数组中的重复项"><a class="markdownIt-Anchor" href="#26-删除有序数组中的重复项"></a> 26. 删除有序数组中的重复项</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给你一个 <strong>非严格递增排列</strong> 的数组 <code>nums</code>，请你 <strong>原地</strong> 删除重复出现的元素，使每个元素只出现一次，返回删除后数组的新长度。<br />元素的相对顺序应该保持一致。<br />不要使用额外的数组空间，你必须在 <strong>原地</strong> 修改输入数组，并在使用 O(1) 额外空间的条件下完成。</p><p><strong>说明</strong>：</p><ul><li>数组已排序。</li><li>返回新长度 <code>slow</code> 后，不需要考虑数组中超出新长度后面的元素。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,1,2]</span><br><span class="line">输出: 2, nums = [1,2,_]</span><br><span class="line">解释: 函数应返回新长度 2，并且原数组的前两个元素被修改为 1, 2。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [0,0,1,1,1,2,2,3,3,4]</span><br><span class="line">输出: 5, nums = [0,1,2,3,4,_,_,_,_,_]</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路快慢指针覆盖法"><a class="markdownIt-Anchor" href="#思路快慢指针覆盖法"></a> 思路：快慢指针（覆盖法）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>如果数组长度为 0，直接返回 0。但代码中未处理空数组，不过题目保证 <code>nums</code> 长度至少为 1。</li><li>初始化慢指针 <code>slow = 1</code>，指向下一个不重复元素应该放置的位置。</li><li>遍历数组，快指针 <code>fast</code> 从 1 开始（0 位置默认保留）：<ul><li>如果 <code>nums[fast] != nums[slow-1]</code>（即当前元素与前一个不重复元素不同），则将 <code>nums[fast]</code> 移到 <code>slow</code> 位置，然后 <code>slow += 1</code>。</li><li>否则，<code>fast</code> 继续前进。</li></ul></li><li>返回 <code>slow</code>，即不重复元素的个数。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">removeDuplicates</span>(<span class="params">self, nums: <span class="type">List</span>[<span class="built_in">int</span>]</span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        slow = <span class="number">1</span></span><br><span class="line">        <span class="keyword">for</span> fast, num <span class="keyword">in</span> <span class="built_in">enumerate</span>(nums):</span><br><span class="line">            <span class="keyword">if</span> fast == <span class="number">0</span>:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">if</span> num != nums[slow - <span class="number">1</span>]:</span><br><span class="line">                nums[slow], nums[fast] = nums[fast], nums[slow]</span><br><span class="line">                slow += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> slow</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，一次遍历。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，原地修改。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的代码使用交换（<code>nums[slow], nums[fast] = nums[fast], nums[slow]</code>）来将不重复元素挪到前面，但更常见的是直接覆盖（<code>nums[slow] = nums[fast]</code>），因为 <code>slow</code> 位置的值不再需要。交换也能工作，但略显冗余。<br /><strong>关键点</strong>：<code>slow</code> 始终指向下一个要写入的位置，而 <code>slow-1</code> 是最后一个已确认的不重复元素。当遇到新元素时，将其放入 <code>slow</code> 并递增 <code>slow</code>。最终返回 <code>slow</code> 即为长度。<br />此解法是去重问题的标准双指针实现，简洁高效。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>找出数组中的所有 K 近邻下标 -LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/10/%E6%89%BE%E5%87%BA%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%20K%20%E8%BF%91%E9%82%BB%E4%B8%8B%E6%A0%87/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/10/%E6%89%BE%E5%87%BA%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%20K%20%E8%BF%91%E9%82%BB%E4%B8%8B%E6%A0%87/</id>
    <published>2026-07-09T16:00:00.000Z</published>
    <updated>2026-07-10T11:36:52.884Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="2200-找出数组中的所有-k-近邻下标"><a class="markdownIt-Anchor" href="#2200-找出数组中的所有-k-近邻下标"></a> 2200. 找出数组中的所有 K 近邻下标</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给你一个下标从 <strong>0</strong> 开始的整数数组 <code>nums</code> 和两个整数 <code>key</code> 和 <code>k</code>。<br /><strong>K 近邻下标</strong> 定义为 <code>nums</code> 中某个下标 <code>i</code>，满足至少存在一个下标 <code>j</code> 使得 <code>|i - j| &lt;= k</code> 且 <code>nums[j] == key</code>。<br />以列表形式返回所有 K 近邻下标，按 <strong>递增顺序</strong> 排序。</p><p><strong>说明</strong>：</p><ul><li>一个下标可能对应多个 <code>key</code> 位置，但只计入一次。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [3,4,9,1,3,3], key = 3, k = 2</span><br><span class="line">输出: [0,1,2,3,4,5]</span><br><span class="line">解释: key 出现在下标 0、4、5，扩展区间 [0,2]、[2,6]、[3,7]，覆盖 [0,5]。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,2,3,4,5], key = 2, k = 1</span><br><span class="line">输出: [0,1,2]</span><br><span class="line">解释: key 在索引 1，区间 [0,2]，因此 0、1、2 都是近邻。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路快慢指针扫描一次遍历线性时间"><a class="markdownIt-Anchor" href="#思路快慢指针扫描一次遍历线性时间"></a> 思路：快慢指针扫描（一次遍历，线性时间）</h3><ul><li><strong>快指针 <code>fast</code></strong>：遍历数组，寻找值为 <code>key</code> 的位置。</li><li><strong>慢指针 <code>slow</code></strong>：始终指向下一个尚未被检查的下标，初始为 0。<br />当 <code>fast</code> 指向 <code>key</code> 时，从 <code>slow</code> 和 <code>fast - k</code> 的较大值开始，依次检查所有满足 <code>|slow - fast| &lt;= k</code> 的下标，并将它们加入结果列表，同时 <code>slow</code> 递增。<br />由于 <code>slow</code> 永不回退，且只向前移动，每个下标最多被访问一次，保证了线性时间复杂度。</li></ul><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>初始化 <code>slow = 0</code>，结果列表 <code>ans = []</code>，数组长度 <code>l</code>。</li><li>遍历 <code>fast</code> 从 0 到 <code>l-1</code>：<ul><li>若 <code>nums[fast] == key</code>：<ul><li>将 <code>slow</code> 更新为 <code>max(slow, fast - k)</code>，确保不会遗漏之前未扫描的下标。</li><li>当 <code>slow &lt;= fast + k</code> 且 <code>slow &lt; l</code> 时：<ul><li>由于已经保证 <code>slow &gt;= fast - k</code>，此时一定有 <code>abs(slow - fast) &lt;= k</code>，直接将 <code>slow</code> 加入 <code>ans</code>。</li><li><code>slow += 1</code>。</li></ul></li></ul></li><li>继续下一个 <code>fast</code>。</li></ul></li><li>返回 <code>ans</code>（已自动升序且无重复）。</li></ol><h4 id="代码实现您的优化代码"><a class="markdownIt-Anchor" href="#代码实现您的优化代码"></a> 代码实现（您的优化代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">findKDistantIndices</span>(<span class="params">self, nums: <span class="type">List</span>[<span class="built_in">int</span>], key: <span class="built_in">int</span>, k: <span class="built_in">int</span></span>) -&gt; <span class="type">List</span>[<span class="built_in">int</span>]:</span><br><span class="line">        slow = <span class="number">0</span></span><br><span class="line">        ans = []</span><br><span class="line">        l = <span class="built_in">len</span>(nums)</span><br><span class="line">        <span class="keyword">for</span> fast, num <span class="keyword">in</span> <span class="built_in">enumerate</span>(nums):</span><br><span class="line">            slow = <span class="built_in">max</span>(slow, fast - k)</span><br><span class="line">            <span class="keyword">while</span> num == key <span class="keyword">and</span> slow &lt;= fast + k <span class="keyword">and</span> slow &lt; l:</span><br><span class="line">                ans.append(slow)</span><br><span class="line">                slow += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> ans</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个下标最多被 <code>slow</code> 访问一次，外层循环 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，用于存储结果列表。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>相比最初的版本，你通过<strong>维护 <code>slow</code> 指针不重置</strong>，并利用 <code>max(slow, fast - k)</code> 跳过已经处理过的下标，将算法从可能的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> 优化为严格的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>。同时，由于 <code>slow</code> 递增，结果自动保持升序，无需额外排序或去重。这是本题非常优雅的线性解法。</p><p><strong>关键点</strong>：</p><ul><li>只有当 <code>nums[fast] == key</code> 时才触发添加，但 <code>slow</code> 的推进不受 <code>key</code> 位置影响，确保覆盖所有可能的下标。</li><li>由于 <code>slow</code> 始终指向下一个可能合法的下标，每个下标只被考虑一次，不会重复。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>有效三角形的个数-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/10/%E6%9C%89%E6%95%88%E4%B8%89%E8%A7%92%E5%BD%A2%E7%9A%84%E4%B8%AA%E6%95%B0/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/10/%E6%9C%89%E6%95%88%E4%B8%89%E8%A7%92%E5%BD%A2%E7%9A%84%E4%B8%AA%E6%95%B0/</id>
    <published>2026-07-09T16:00:00.000Z</published>
    <updated>2026-07-10T11:48:59.765Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="611-有效三角形的个数"><a class="markdownIt-Anchor" href="#611-有效三角形的个数"></a> 611. 有效三角形的个数</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个包含非负整数的数组 <code>nums</code>，返回其中可以组成三角形三条边的三元组个数。<br />三角形的条件是：任意两边之和大于第三边，等价于<strong>较短两边之和大于最长边</strong>。</p><p><strong>说明</strong>：</p><ul><li>三元组 <code>(i, j, k)</code> 满足 <code>i &lt; j &lt; k</code>，且 <code>nums[i] + nums[j] &gt; nums[k]</code>。</li><li>数组长度 <code>1 &lt;= n &lt;= 1000</code>。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [2,2,3,4]</span><br><span class="line">输出: 3</span><br><span class="line">解释: 有效组合: (2,2,3), (2,3,4), (2,3,4) 共 3 个。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [4,2,3,4]</span><br><span class="line">输出: 4</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路排序-双指针固定最短边滑动中边"><a class="markdownIt-Anchor" href="#思路排序-双指针固定最短边滑动中边"></a> 思路：排序 + 双指针（固定最短边，滑动中边）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将数组 <code>nums</code> 升序排序。</li><li>外层循环 <code>slow</code> 从 0 到 <code>n-3</code>，固定最短边 <code>num = nums[slow]</code>。<br />如果 <code>num == 0</code>，由于三角形边长必须为正数，直接跳过。</li><li>初始化 <code>mid = slow + 1</code>，指向可能的次短边（中边）的起始位置。</li><li>内层循环 <code>fast</code> 从 <code>slow+2</code> 到 <code>n-1</code>，枚举最长边 <code>c = nums[fast]</code>：<ul><li>当 <code>nums[fast] - nums[mid] &gt;= num</code> 时，说明 <code>num + nums[mid] &lt;= nums[fast]</code>，不满足三角形条件（两边之和不大于第三边）。此时将 <code>mid</code> 右移，直到满足 <code>num + nums[mid] &gt; nums[fast]</code>。</li><li>此时，所有下标从 <code>mid</code> 到 <code>fast-1</code> 的元素都可以作为中边 <code>b</code>，与当前的 <code>num</code> 和 <code>nums[fast]</code> 组成三角形。<br />可行数量为 <code>fast - mid</code>，累加到答案 <code>ans</code>。</li></ul></li><li>返回 <code>ans</code>。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">triangleNumber</span>(<span class="params">self, nums: <span class="type">List</span>[<span class="built_in">int</span>]</span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        nums.sort()</span><br><span class="line">        n = <span class="built_in">len</span>(nums)</span><br><span class="line">        ans = <span class="number">0</span></span><br><span class="line">        <span class="keyword">for</span> slow <span class="keyword">in</span> <span class="built_in">range</span>(n - <span class="number">2</span>):</span><br><span class="line">            num = nums[slow]</span><br><span class="line">            <span class="keyword">if</span> num == <span class="number">0</span>:  </span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            mid = slow + <span class="number">1</span></span><br><span class="line">            <span class="keyword">for</span> fast <span class="keyword">in</span> <span class="built_in">range</span>(slow + <span class="number">2</span>, n):</span><br><span class="line">                <span class="keyword">while</span> nums[fast] - nums[mid] &gt;= num:</span><br><span class="line">                    mid += <span class="number">1</span></span><br><span class="line">                ans += fast - mid</span><br><span class="line">        <span class="keyword">return</span> ans</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li><strong>时间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>。外层循环 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，内层循环中 <code>fast</code> 和 <code>mid</code> 均单调递增，每个指针最多移动 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> 次，总体 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>。</li><li><strong>空间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>（忽略排序占用的栈空间）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的命名 <code>slow</code>, <code>mid</code>, <code>fast</code> 非常形象：</p><ul><li><code>slow</code> 是<strong>最短边</strong>，固定不变；</li><li><code>fast</code> 是<strong>最长边</strong>，不断右移；</li><li><code>mid</code> 是<strong>中边</strong>，随着 <code>fast</code> 增大和 <code>num</code> 固定，为了满足 <code>num + nums[mid] &gt; nums[fast]</code>，<code>mid</code> 只能向右移动，绝不回头。</li></ul><p>利用排序后的单调性，<code>mid</code> 的单向移动保证了内层循环的摊还复杂度为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，整体达到 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，这是本题的标准最优解法。跳过 <code>0</code> 的处理也避免了对无效边长进行多余计算，代码逻辑清晰且高效。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>删除字符串两端相同字符后的最短长度-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/09/%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%A4%E7%AB%AF%E7%9B%B8%E5%90%8C%E5%AD%97%E7%AC%A6%E5%90%8E%E7%9A%84%E6%9C%80%E7%9F%AD%E9%95%BF%E5%BA%A6/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/09/%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%A4%E7%AB%AF%E7%9B%B8%E5%90%8C%E5%AD%97%E7%AC%A6%E5%90%8E%E7%9A%84%E6%9C%80%E7%9F%AD%E9%95%BF%E5%BA%A6/</id>
    <published>2026-07-08T16:00:00.000Z</published>
    <updated>2026-07-09T12:20:08.110Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1750-删除字符串两端相同字符后的最短长度"><a class="markdownIt-Anchor" href="#1750-删除字符串两端相同字符后的最短长度"></a> 1750. 删除字符串两端相同字符后的最短长度</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给你一个只包含字符 <code>'a'</code>、<code>'b'</code> 和 <code>'c'</code> 的字符串 <code>s</code>，你可以执行以下操作任意次：</p><ul><li>选择字符串的一个非空前缀和一个非空后缀，要求前缀和后缀的<strong>所有字符都相同</strong>，且前缀和后缀的字符相同（即 <code>s[0] == s[-1]</code>）。然后删除前缀和后缀，剩余部分拼接成新字符串。<br />求在执行任意次操作后，字符串可能的最短长度。</li></ul><p><strong>说明</strong>：</p><ul><li>每次操作必须同时删除前缀和后缀，且前缀和后缀的字符必须相同。</li><li>如果两端字符不同，则无法继续操作，返回当前长度。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;ca&quot;</span><br><span class="line">输出: 2</span><br><span class="line">解释: 两端字符 &#x27;c&#x27; 和 &#x27;a&#x27; 不同，无法删除，长度为 2。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;cabaabac&quot;</span><br><span class="line">输出: 0</span><br><span class="line">解释: </span><br><span class="line">  初始: &quot;cabaabac&quot; 两端都为 &#x27;c&#x27;，删除所有 &#x27;c&#x27; 得到 &quot;abaaba&quot;。</span><br><span class="line">  现在两端为 &#x27;a&#x27; 和 &#x27;a&#x27;，删除所有 &#x27;a&#x27; 得到 &quot;b&quot;。</span><br><span class="line">  但注意：删除前缀和后缀时要求它们连续相同，实际上 &quot;abaaba&quot; 两端为 &#x27;a&#x27;，删除所有连续 &#x27;a&#x27; 后，中间 &quot;b&quot; 长度为1，但最后两端 &#x27;b&#x27; 不同，结果应为1？实际上标准答案是0？需要核对。根据LeetCode示例，输入 &quot;cabaabac&quot; 输出 0，因为删除两端相同字符块后最终为空。</span><br></pre></td></tr></table></figure><ul><li>示例 3：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;aabccabba&quot;</span><br><span class="line">输出: 3</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针删除相同字符块"><a class="markdownIt-Anchor" href="#思路双指针删除相同字符块"></a> 思路：双指针删除相同字符块</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>初始化左指针 <code>left = 0</code>，右指针 <code>right = len(s) - 1</code>。</li><li>当 <code>left &lt; right</code> 且 <code>s[left] == s[right]</code> 时：<ul><li>记录当前字符 <code>ch = s[left]</code>。</li><li>从左边删除所有与 <code>ch</code> 相同的连续字符：<code>while left &lt;= right and s[left] == ch: left += 1</code>。</li><li>从右边删除所有与 <code>ch</code> 相同的连续字符：<code>while left &lt;= right and s[right] == ch: right -= 1</code>。</li></ul></li><li>循环结束后，若 <code>left &gt; right</code>，说明全部删除，返回 0；否则返回 <code>right - left + 1</code>。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">minimumLength</span>(<span class="params">self, s: <span class="built_in">str</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        left, right = <span class="number">0</span>, <span class="built_in">len</span>(s) - <span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> left &lt; right <span class="keyword">and</span> s[left] == s[right]:</span><br><span class="line">            ch = s[left]</span><br><span class="line">            <span class="keyword">while</span> left &lt;= right <span class="keyword">and</span> s[left] == ch:</span><br><span class="line">                left += <span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> left &lt;= right <span class="keyword">and</span> s[right] == ch:</span><br><span class="line">                right -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> right - left + <span class="number">1</span> <span class="keyword">if</span> left &lt;= right <span class="keyword">else</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个字符最多被访问一次。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，仅使用常数个变量。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的代码正确实现了题意：每次从两端删除所有与当前相同字符的连续块，并继续检查新的两端。注意内层两个 <code>while</code> 循环分别删除左右两侧的相同字符块，确保删除的是前缀和后缀中所有连续的该字符。最终若指针交错则返回 0，否则返回剩余长度。该解法是本题的标准双指针解法，高效且简洁。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>反转一个字符串里的字母后反转特殊字符-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/09/%E5%8F%8D%E8%BD%AC%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%AD%97%E6%AF%8D%E5%90%8E%E5%8F%8D%E8%BD%AC%E7%89%B9%E6%AE%8A%E5%AD%97%E7%AC%A6/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/09/%E5%8F%8D%E8%BD%AC%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%AD%97%E6%AF%8D%E5%90%8E%E5%8F%8D%E8%BD%AC%E7%89%B9%E6%AE%8A%E5%AD%97%E7%AC%A6/</id>
    <published>2026-07-08T16:00:00.000Z</published>
    <updated>2026-07-09T11:49:15.523Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="3823-反转一个字符串里的字母后反转特殊字符"><a class="markdownIt-Anchor" href="#3823-反转一个字符串里的字母后反转特殊字符"></a> 3823. 反转一个字符串里的字母后反转特殊字符</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个字符串 <code>s</code>，该字符串只包含小写字母和特殊字符 <code>&quot;!@#$%^&amp;*()&quot;</code>。<br />请先反转字符串中所有字母的顺序（但保持字母在原字母位置），再反转所有特殊字符的顺序（保持特殊字符在原来特殊字符位置），然后返回结果字符串。</p><p><strong>说明</strong>：</p><ul><li>字母和特殊字符互不干扰，各自反转。</li><li>反转是指将同类字符的顺序倒置。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;)ebc#da@f(&quot;</span><br><span class="line">输出: &quot;(fad@cb#e)&quot;</span><br><span class="line">解释: </span><br><span class="line">  1. 提取字母 &quot;ebc daf&quot;？实际上字母按顺序为 e,b,c,d,a,f → 反转后 f,a,d,c,b,e。</span><br><span class="line">  2. 特殊字符顺序为 ),#,@,( → 反转后 (, @, #, )。</span><br><span class="line">  3. 填充后得到 &quot;(fad@cb#e)&quot;。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;z&quot;</span><br><span class="line">输出: &quot;z&quot;</span><br></pre></td></tr></table></figure><ul><li>示例 3：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;!@#$%^&amp;*()&quot;</span><br><span class="line">输出: &quot;)(*&amp;^%$#@!&quot;</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针分两轮反转通用方法"><a class="markdownIt-Anchor" href="#思路双指针分两轮反转通用方法"></a> 思路：双指针分两轮反转（通用方法）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将字符串转为列表 <code>t</code>，便于原地修改。</li><li>定义通用函数 <code>reverse(t, f)</code>，其中 <code>f</code> 是一个判断函数，用于保护某些字符不参与交换：<ul><li>设置左右指针 <code>i=0</code>，<code>j=len(t)-1</code>。</li><li>当 <code>i &lt; j</code> 时：<ul><li>左指针跳过满足 <code>f</code> 的字符（即保护该类字符）。</li><li>右指针跳过满足 <code>f</code> 的字符。</li><li>交换左右指针所指的字符（这两个字符都不满足 <code>f</code>，即属于另一类）。</li><li>移动指针。</li></ul></li><li>该函数的效果是：将所有<strong>不满足</strong> <code>f</code> 的字符进行反转，而满足 <code>f</code> 的字符保持位置不变。</li></ul></li><li>第一轮：调用 <code>reverse(t, str.islower)</code>，保护小写字母，反转特殊字符。</li><li>第二轮：调用 <code>reverse(t, lambda ch: not ch.islower())</code>，保护特殊字符，反转小写字母。</li><li>返回列表转字符串。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span>, <span class="type">Callable</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reverse</span>(<span class="params">self, t: <span class="type">List</span>[<span class="built_in">str</span>], f: <span class="type">Callable</span>[[<span class="built_in">str</span>], <span class="built_in">bool</span>]</span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        i, j = <span class="number">0</span>, <span class="built_in">len</span>(t) - <span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> i &lt; j:</span><br><span class="line">            <span class="keyword">while</span> i &lt; j <span class="keyword">and</span> f(t[i]):</span><br><span class="line">                i += <span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> i &lt; j <span class="keyword">and</span> f(t[j]):</span><br><span class="line">                j -= <span class="number">1</span></span><br><span class="line">            t[i], t[j] = t[j], t[i]</span><br><span class="line">            i += <span class="number">1</span></span><br><span class="line">            j -= <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reverseByType</span>(<span class="params">self, s: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        t = <span class="built_in">list</span>(s)</span><br><span class="line">        <span class="variable language_">self</span>.reverse(t, <span class="built_in">str</span>.islower)          <span class="comment"># 反转非小写（即特殊字符）</span></span><br><span class="line">        <span class="variable language_">self</span>.reverse(t, <span class="keyword">lambda</span> ch: <span class="keyword">not</span> ch.islower())  <span class="comment"># 反转小写字母</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;&#x27;</span>.join(t)</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，两次遍历，每次 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，列表存储字符串。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的解法通过两轮反转，巧妙地实现了分别反转两类字符的需求。在第一轮中，保护小写字母（<code>str.islower</code> 返回 <code>True</code>），于是所有非小写字符（特殊字符）被反转；第二轮保护非小写字符（<code>not ch.islower()</code> 返回 <code>True</code>），于是小写字母被反转。这种“保护-反转”模式通用且高效，避免了显式提取和填充。虽然相较于提取反转再填回的方法，它的理解成本稍高，但代码简洁，且原地操作节省了额外列表。对于本题，两种方法均可，您的实现展现了双指针的灵活运用。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>字典序最小回文串-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/09/%E5%AD%97%E5%85%B8%E5%BA%8F%E6%9C%80%E5%B0%8F%E5%9B%9E%E6%96%87%E4%B8%B2/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/09/%E5%AD%97%E5%85%B8%E5%BA%8F%E6%9C%80%E5%B0%8F%E5%9B%9E%E6%96%87%E4%B8%B2/</id>
    <published>2026-07-08T16:00:00.000Z</published>
    <updated>2026-07-09T11:56:09.501Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="2697-字典序最小回文串"><a class="markdownIt-Anchor" href="#2697-字典序最小回文串"></a> 2697. 字典序最小回文串</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个由小写英文字母组成的字符串 <code>s</code>，你每次操作可以将任意一个字符修改为任意一个小写字母。<br />请你返回将 <code>s</code> 变成回文串所需的最少操作次数？不，本题要求<strong>构造</strong>一个字典序最小的回文串，即通过修改某些字符（不限制操作次数），使得最终字符串是回文串，且字典序最小。</p><p><strong>说明</strong>：</p><ul><li>字典序比较：按从左到右的顺序比较字符。</li><li>回文串：正读反读相同。</li><li>可以修改任意位置的字符为任意小写字母。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;egcfe&quot;</span><br><span class="line">输出: &quot;efcfe&quot;</span><br><span class="line">解释: 将 &#x27;g&#x27; 改为 &#x27;f&#x27;，得到 &quot;efcfe&quot; 是回文串，且字典序最小。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;abcd&quot;</span><br><span class="line">输出: &quot;abba&quot;</span><br><span class="line">解释: 将 &#x27;c&#x27; 改为 &#x27;b&#x27;，&#x27;d&#x27; 改为 &#x27;a&#x27;，得到 &quot;abba&quot; 是回文串，且字典序最小。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针-贪心修改"><a class="markdownIt-Anchor" href="#思路双指针-贪心修改"></a> 思路：双指针 + 贪心修改</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将字符串转为列表 <code>st</code>，便于原地修改。</li><li>设置左指针 <code>left = 0</code>，右指针 <code>right = len(s) - 1</code>。</li><li>当 <code>left &lt; right</code> 时：<ul><li>如果 <code>st[left] != st[right]</code>，则取两者中较小的字符 <code>ch = min(st[left], st[right])</code>，将两端都改为 <code>ch</code>（因为字典序要最小，应尽可能让左边的字符小）。</li><li>移动指针：<code>left += 1</code>，<code>right -= 1</code>。</li></ul></li><li>返回列表转字符串。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">makeSmallestPalindrome</span>(<span class="params">self, s: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        left, right = <span class="number">0</span>, <span class="built_in">len</span>(s) - <span class="number">1</span></span><br><span class="line">        st = <span class="built_in">list</span>(s)</span><br><span class="line">        <span class="keyword">while</span> left &lt; right:</span><br><span class="line">            <span class="keyword">while</span> st[left] != st[right]:</span><br><span class="line">                ch = <span class="built_in">min</span>(st[left], st[right])</span><br><span class="line">                st[left] = ch</span><br><span class="line">                st[right] = ch</span><br><span class="line">            left += <span class="number">1</span></span><br><span class="line">            right -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&quot;</span>.join(st)</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个字符访问一次。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，列表存储字符串（可原地修改）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的代码使用内层 <code>while</code> 循环来确保两端字符相等，实际上由于每次只会修改一次，内层循环最多执行一次，可以简化为 <code>if</code> 判断。但当前写法逻辑正确且能通过。<br /><strong>关键点</strong>：将两端都改为较小的字符，使得字典序最小。例如 <code>'e'</code> 和 <code>'g'</code> 改为 <code>'e'</code>，因为左边字符越小字典序越小。<br />该解法是本题的标准贪心策略，简洁高效。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>给植物浇水 II-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/09/%E7%BB%99%E6%A4%8D%E7%89%A9%E6%B5%87%E6%B0%B4%20II/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/09/%E7%BB%99%E6%A4%8D%E7%89%A9%E6%B5%87%E6%B0%B4%20II/</id>
    <published>2026-07-08T16:00:00.000Z</published>
    <updated>2026-07-09T12:42:39.713Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="2105-给植物浇水-ii"><a class="markdownIt-Anchor" href="#2105-给植物浇水-ii"></a> 2105. 给植物浇水 II</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />Alice 和 Bob 计划给一排植物浇水。植物排成一行，从左到右编号为 <code>0</code> 到 <code>n-1</code>。<br />Alice 从最左边（植物 <code>0</code>）开始，Bob 从最右边（植物 <code>n-1</code>）开始，他们同时向中间移动。</p><ul><li>Alice 的水壶容量为 <code>capacityA</code>，Bob 的水壶容量为 <code>capacityB</code>。</li><li>当他们到达一株植物时，如果水壶中的水不足以浇灌该植物，他们必须<strong>重新装满</strong>自己的水壶（在当前位置），然后才能浇水。</li><li>他们各自独立行动，但每次只能浇一株植物。</li><li>如果两人同时到达同一株植物（即中间只剩一株植物），则水壶中剩余水量较多的人去浇灌（如果两人水量相同，Alice 去浇）。</li></ul><p>求他们总共需要重新装满水壶的次数。</p><p><strong>说明</strong>：</p><ul><li>初始水壶是满的（容量分别为 <code>capacityA</code> 和 <code>capacityB</code>）。</li><li>植物需要的水量由 <code>plants[i]</code> 给出。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: plants = [2,2,3,3], capacityA = 5, capacityB = 5</span><br><span class="line">输出: 1</span><br><span class="line">解释:</span><br><span class="line">  Alice 浇 0 号 (2)，剩 3；Bob 浇 3 号 (3)，剩 2。</span><br><span class="line">  Alice 浇 1 号 (2)，剩 1；Bob 浇 2 号 (3)，水量不够，Bob 重新装满（+1），然后浇 2 号。</span><br><span class="line">  总共 1 次。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">输入: plants = [2,2,3,3], capacityA = 3, capacityB = 4</span><br><span class="line">输出: 2</span><br><span class="line">解释:</span><br><span class="line">  Alice 浇 0 号 (2)，剩 1；Bob 浇 3 号 (3)，剩 1。</span><br><span class="line">  Alice 浇 1 号 (2)，水不够，重新装满（+1），剩 3？实际需先装满再浇，所以重新装满后浇 1 号，剩 1。</span><br><span class="line">  Bob 浇 2 号 (3)，水不够，重新装满（+1），然后浇 2 号。</span><br><span class="line">  总共 2 次。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针模拟"><a class="markdownIt-Anchor" href="#思路双指针模拟"></a> 思路：双指针模拟</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>初始化左右指针 <code>left = 0</code>，<code>right = len(plants) - 1</code>，Alice 当前水量 <code>a = capacityA</code>，Bob 当前水量 <code>b = capacityB</code>，答案 <code>ans = 0</code>。</li><li>当 <code>left &lt; right</code> 时：<ul><li>Alice 浇水：<ul><li>如果 <code>a &lt; plants[left]</code>，则需要重新装满（<code>a = capacityA</code>，<code>ans += 1</code>）。</li><li>浇水：<code>a -= plants[left]</code>，<code>left += 1</code>。</li></ul></li><li>Bob 浇水：<ul><li>如果 <code>b &lt; plants[right]</code>，则需要重新装满（<code>b = capacityB</code>，<code>ans += 1</code>）。</li><li>浇水：<code>b -= plants[right]</code>，<code>right -= 1</code>。</li></ul></li></ul></li><li>如果 <code>left == right</code>（中间只剩一株植物），则由水量较多的人浇灌：<ul><li>如果 <code>max(a, b) &lt; plants[left]</code>，则其中一人需要重新装满（<code>ans += 1</code>）。</li><li>不需要实际修改水量，因为已经完成所有浇水。</li></ul></li><li>返回 <code>ans</code>。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">minimumRefill</span>(<span class="params">self, plants: <span class="type">List</span>[<span class="built_in">int</span>], capacityA: <span class="built_in">int</span>, capacityB: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        left, right = <span class="number">0</span>, <span class="built_in">len</span>(plants) - <span class="number">1</span></span><br><span class="line">        a, b = capacityA, capacityB</span><br><span class="line">        ans = <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> left &lt; right:</span><br><span class="line">            <span class="keyword">if</span> a &lt; plants[left]:</span><br><span class="line">                a = capacityA</span><br><span class="line">                ans += <span class="number">1</span></span><br><span class="line">            a -= plants[left]</span><br><span class="line">            left += <span class="number">1</span></span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span> b &lt; plants[right]:</span><br><span class="line">                b = capacityB</span><br><span class="line">                ans += <span class="number">1</span></span><br><span class="line">            b -= plants[right]</span><br><span class="line">            right -= <span class="number">1</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> left == right:</span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">max</span>(a, b) &lt; plants[left]:</span><br><span class="line">                ans += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> ans</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个植物访问一次。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，仅使用常数个变量。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的代码正确模拟了两人同时从两端浇水的过程。关键点：</p><ul><li>每次浇水前检查当前水量是否足够，若不足则重新装满并计数。</li><li>对于最后一株植物，由水量较多者负责，只需判断较大水量是否足够，若不够则只需加一次（因为只需一个人去浇）。</li><li>该解法是本题的标准双指针模拟，逻辑清晰，运行高效。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>反转单词前缀-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/07/%E5%8F%8D%E8%BD%AC%E5%8D%95%E8%AF%8D%E5%89%8D%E7%BC%80/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/07/%E5%8F%8D%E8%BD%AC%E5%8D%95%E8%AF%8D%E5%89%8D%E7%BC%80/</id>
    <published>2026-07-06T16:00:00.000Z</published>
    <updated>2026-07-07T06:14:51.951Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="2000-反转单词前缀"><a class="markdownIt-Anchor" href="#2000-反转单词前缀"></a> 2000. 反转单词前缀</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个字符串 <code>word</code> 和一个字符 <code>ch</code>，找出 <code>ch</code> 在 <code>word</code> 中第一次出现的位置（下标从 0 开始）。反转从位置 0 到该位置（含）的所有字符，并返回反转后的字符串。如果 <code>ch</code> 不存在于 <code>word</code> 中，则返回原字符串。</p><p><strong>说明</strong>：</p><ul><li>只需要反转前缀，不修改后缀。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: word = &quot;abcdefd&quot;, ch = &quot;d&quot;</span><br><span class="line">输出: &quot;dcbaefd&quot;</span><br><span class="line">解释: &quot;d&quot; 第一次出现在下标 3，反转前 4 个字符 &quot;abcd&quot; → &quot;dcba&quot;。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: word = &quot;xyxzxe&quot;, ch = &quot;z&quot;</span><br><span class="line">输出: &quot;zxyxxe&quot;</span><br><span class="line">解释: &quot;z&quot; 第一次出现在下标 2，反转前 3 个字符 &quot;xyz&quot; → &quot;zyx&quot;。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针原地反转"><a class="markdownIt-Anchor" href="#思路双指针原地反转"></a> 思路：双指针原地反转</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>使用 <code>while</code> 循环查找字符 <code>ch</code> 第一次出现的下标 <code>i</code>。若未找到（<code>i == len(word)</code>），直接返回原字符串。</li><li>将字符串转为列表 <code>ans</code>，便于原地修改。</li><li>设置左指针 <code>left = 0</code>，右指针 <code>right = i</code>。</li><li>交换 <code>ans[left]</code> 和 <code>ans[right]</code>，然后 <code>left++</code>，<code>right--</code>，直到 <code>left &gt;= right</code>。</li><li>将列表转换回字符串并返回。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reversePrefix</span>(<span class="params">self, word: <span class="built_in">str</span>, ch: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        i = <span class="number">0</span></span><br><span class="line">        l = <span class="built_in">len</span>(word)</span><br><span class="line">        <span class="keyword">while</span> i &lt; l:</span><br><span class="line">            <span class="keyword">if</span> word[i] == ch:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            i += <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> i == l:</span><br><span class="line">            <span class="keyword">return</span> word</span><br><span class="line">        left = <span class="number">0</span></span><br><span class="line">        right = i</span><br><span class="line">        ans = <span class="built_in">list</span>(word)</span><br><span class="line">        <span class="keyword">while</span> left &lt; right:</span><br><span class="line">            ans[left], ans[right] = ans[right], ans[left]</span><br><span class="line">            left += <span class="number">1</span></span><br><span class="line">            right -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&quot;</span>.join(ans)</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span> 是字符串长度，查找和反转各需线性时间。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，列表占用 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span> 字符空间（但原地交换，比切片节省临时字符串）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的代码使用双指针进行原地反转，避免了切片产生的额外字符串副本（虽然列表也有开销，但通常更高效）。查找过程手动实现，逻辑清晰。如果使用 Python 内置方法 <code>find</code> 和切片可进一步简化，但您的实现更具通用性，且易于理解。边界情况（未找到字符）处理正确，直接返回原字符串。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>反转字符串前缀 -LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/07/%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%89%8D%E7%BC%80/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/07/%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%89%8D%E7%BC%80/</id>
    <published>2026-07-06T16:00:00.000Z</published>
    <updated>2026-07-07T06:04:17.396Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="3794-反转字符串前缀-reverse-string-prefix"><a class="markdownIt-Anchor" href="#3794-反转字符串前缀-reverse-string-prefix"></a> 3794. 反转字符串前缀 - Reverse String Prefix</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个字符串 <code>s</code> 和一个整数 <code>k</code>，将字符串的<strong>前 <code>k</code> 个字符</strong>进行反转。其余字符保持原来的顺序。返回反转后的字符串。</p><p><strong>说明</strong>：</p><ul><li><code>1 &lt;= s.length &lt;= 100</code></li><li><code>0 &lt;= k &lt;= s.length</code>（根据常见实现推断，可自行验证）</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;abcd&quot;, k = 2</span><br><span class="line">输出: &quot;bacd&quot;</span><br><span class="line">解释: 前 2 个字符 &quot;ab&quot; 反转为 &quot;ba&quot;，得到 &quot;bacd&quot;。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;xyz&quot;, k = 3</span><br><span class="line">输出: &quot;zyx&quot;</span><br><span class="line">解释: 前 3 个字符即整个字符串反转。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路字符串切片拼接"><a class="markdownIt-Anchor" href="#思路字符串切片拼接"></a> 思路：字符串切片拼接</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将字符串 <code>s</code> 分成两部分：前缀 <code>pre = s[:k]</code>（前 k 个字符）和后缀 <code>suf = s[k:]</code>（剩余字符）。</li><li>反转前缀：<code>pre[::-1]</code>。</li><li>将反转后的前缀与后缀拼接并返回。</li></ol><h4 id="代码实现"><a class="markdownIt-Anchor" href="#代码实现"></a> 代码实现</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reversePrefix</span>(<span class="params">self, s: <span class="built_in">str</span>, k: <span class="built_in">int</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        pre = s[:k]</span><br><span class="line">        suf = s[k:]</span><br><span class="line">        <span class="keyword">return</span> pre[::-<span class="number">1</span>] + suf</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.43056em;vertical-align:0em;"></span><span class="mord mathnormal">n</span></span></span></span> 是字符串长度。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，切片创建新字符串。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>你的代码简洁高效，但如果能注意变量命名的可读性（如将 <code>od</code> 改为 <code>suf</code> 表示后缀），代码会更清晰。<br /><strong>边界情况</strong>：由于 Python 切片特性，当 <code>k = 0</code> 时，<code>s[:0]</code> 为空字符串，<code>s[0:]</code> 为原字符串，结果正确；当 <code>k &gt;= len(s)</code> 时，<code>s[:k]</code> 返回整个字符串，<code>s[k:]</code> 为空，结果也正确。无需额外判断。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>垂直翻转子矩阵-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/07/%E5%9E%82%E7%9B%B4%E7%BF%BB%E8%BD%AC%E5%AD%90%E7%9F%A9%E9%98%B5/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/07/%E5%9E%82%E7%9B%B4%E7%BF%BB%E8%BD%AC%E5%AD%90%E7%9F%A9%E9%98%B5/</id>
    <published>2026-07-06T16:00:00.000Z</published>
    <updated>2026-07-07T06:35:19.745Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="3643-垂直翻转子矩阵"><a class="markdownIt-Anchor" href="#3643-垂直翻转子矩阵"></a> 3643. 垂直翻转子矩阵</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给你一个 <code>m x n</code> 的整数矩阵 <code>grid</code>，以及三个整数 <code>x</code>、<code>y</code> 和 <code>k</code>。<br /><code>x</code> 和 <code>y</code> 表示一个 <strong>正方形子矩阵</strong> 的左上角下标，<code>k</code> 表示该正方形子矩阵的边长。<br />你的任务是<strong>垂直翻转</strong>该子矩阵的行顺序（即上下翻转），并返回更新后的矩阵。</p><p><strong>说明</strong>：</p><ul><li>垂直翻转：子矩阵的第一行与最后一行交换，第二行与倒数第二行交换，以此类推。</li><li>子矩阵外的元素保持不变。</li><li>你可以直接在原矩阵上修改，也可以使用额外空间。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: grid = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], x = 1, y = 0, k = 3</span><br><span class="line">输出: [[1,2,3,4],[13,14,15,8],[9,10,11,12],[5,6,7,16]]</span><br><span class="line">解释: 目标子矩阵为行 1-3、列 0-2。翻转前行顺序为：[5,6,7]、[9,10,11]、[13,14,15]；翻转后行顺序变为：[13,14,15]、[9,10,11]、[5,6,7]，列3保持不变。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: grid = [[3,4,2,3],[2,3,4,2]], x = 0, y = 2, k = 2</span><br><span class="line">输出: [[3,4,4,2],[2,3,2,3]]</span><br><span class="line">解释: 目标子矩阵为行 0-1、列 2-3。翻转前为 [[2,3],[4,2]]，翻转后变为 [[4,2],[2,3]]。</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路双指针逐行交换原地修改"><a class="markdownIt-Anchor" href="#思路双指针逐行交换原地修改"></a> 思路：双指针逐行交换（原地修改）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li><strong>确定子矩阵边界</strong>：<ul><li>顶部行：<code>top = x</code></li><li>底部行：<code>bottom = x + k - 1</code></li></ul></li><li><strong>双指针交换行</strong>：<ul><li>当 <code>top &lt; bottom</code> 时，进行循环：<ul><li>遍历子矩阵的每一列 <code>j</code>，范围从 <code>y</code> 到 <code>y + k - 1</code>。</li><li>交换 <code>grid[top][j]</code> 和 <code>grid[bottom][j]</code> 的值。</li><li>交换完成后，<code>top</code> 向下移动一行（<code>top++</code>），<code>bottom</code> 向上移动一行（<code>bottom--</code>）。</li></ul></li></ul></li><li><strong>返回结果</strong>：原地修改矩阵后，直接返回 <code>grid</code>。</li></ol><h4 id="代码实现你的代码"><a class="markdownIt-Anchor" href="#代码实现你的代码"></a> 代码实现（你的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reverseSubmatrix</span>(<span class="params">self, grid: <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]], x: <span class="built_in">int</span>, y: <span class="built_in">int</span>, k: <span class="built_in">int</span></span>) -&gt; <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]]:</span><br><span class="line">        <span class="comment"># 初始化上下两个行指针</span></span><br><span class="line">        l, r = x, x + k - <span class="number">1</span></span><br><span class="line">        <span class="keyword">while</span> l &lt; r:</span><br><span class="line">            <span class="comment"># 交换当前两行在子矩阵范围内的所有元素</span></span><br><span class="line">            <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(y, y + k):</span><br><span class="line">                grid[l][j], grid[r][j] = grid[r][j], grid[l][j]</span><br><span class="line">            <span class="comment"># 指针向中间移动</span></span><br><span class="line">            l += <span class="number">1</span></span><br><span class="line">            r -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> grid</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li><strong>时间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>k</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(k^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>，因为需要遍历子矩阵中一半的行（<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">k/2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mord">/</span><span class="mord">2</span></span></span></span> 行），每行有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.69444em;vertical-align:0em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span> 个元素需要交换。</li><li><strong>空间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，仅使用了有限的几个指针变量，在原矩阵上直接进行修改。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>你的解法非常经典，直接利用了双指针（或称为对撞指针）的思想。它通过同时从子矩阵的顶部和底部向中间遍历，并交换对应行的所有元素，高效地完成了垂直翻转。</p><p><strong>优点</strong>：</p><ul><li><strong>原地修改</strong>：没有使用额外的矩阵空间，内存效率高。</li><li><strong>逻辑清晰</strong>：代码直观地反映了“上下交换行”这一操作本质。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>翻转图像-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/07/%E7%BF%BB%E8%BD%AC%E5%9B%BE%E5%83%8F/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/07/%E7%BF%BB%E8%BD%AC%E5%9B%BE%E5%83%8F/</id>
    <published>2026-07-06T16:00:00.000Z</published>
    <updated>2026-07-07T06:57:45.019Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="832-翻转图像"><a class="markdownIt-Anchor" href="#832-翻转图像"></a> 832. 翻转图像</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个二进制矩阵 <code>image</code>，先水平翻转每一行（即反转每一行的顺序），然后反转每个像素（0 变为 1，1 变为 0）。返回结果图像。</p><p><strong>说明</strong>：</p><ul><li>水平翻转：例如 <code>[1,1,0]</code> 变为 <code>[0,1,1]</code>。</li><li>反转像素：<code>0</code> → <code>1</code>，<code>1</code> → <code>0</code>。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: image = [[1,1,0],[1,0,1],[0,0,0]]</span><br><span class="line">输出: [[1,0,0],[0,1,0],[1,1,1]]</span><br><span class="line">解释: 先水平翻转每行得到 [[0,1,1],[1,0,1],[0,0,0]]，再反转像素得到结果。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: image = [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]</span><br><span class="line">输出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路一分两步水平翻转-反转像素"><a class="markdownIt-Anchor" href="#思路一分两步水平翻转-反转像素"></a> 思路一：分两步（水平翻转 + 反转像素）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>遍历每一行 <code>i</code>，将该行水平翻转：<code>image[i] = image[i][::-1]</code>。</li><li>再次遍历每一行和每个元素，将 <code>0</code> 变为 <code>1</code>，<code>1</code> 变为 <code>0</code>（或使用 <code>1 - x</code>）。</li><li>返回修改后的 <code>image</code>。</li></ol><h4 id="思路一代码实现"><a class="markdownIt-Anchor" href="#思路一代码实现"></a> 思路一：代码实现</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">flipAndInvertImage</span>(<span class="params">self, image: <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]]</span>) -&gt; <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]]:</span><br><span class="line">        m, n = <span class="built_in">len</span>(image), <span class="built_in">len</span>(image[<span class="number">0</span>])</span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(m):</span><br><span class="line">            image[i] = image[i][::-<span class="number">1</span>]</span><br><span class="line">        <span class="keyword">for</span> i, row <span class="keyword">in</span> <span class="built_in">enumerate</span>(image):</span><br><span class="line">            <span class="keyword">for</span> j, x <span class="keyword">in</span> <span class="built_in">enumerate</span>(row):</span><br><span class="line">                image[i][j] = <span class="number">1</span> <span class="keyword">if</span> x == <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">        <span class="keyword">return</span> image</span><br></pre></td></tr></table></figure><hr /><h3 id="思路二合并一步双指针同时交换并反转"><a class="markdownIt-Anchor" href="#思路二合并一步双指针同时交换并反转"></a> 思路二：合并一步（双指针同时交换并反转）</h3><h4 id="算法步骤-2"><a class="markdownIt-Anchor" href="#算法步骤-2"></a> 算法步骤</h4><ol><li>遍历每一行，使用双指针 <code>l</code>（左）和 <code>r</code>（右）指向行首和行尾。</li><li>循环条件 <code>l &lt;= r</code>（当 <code>l == r</code> 时，只需处理中间元素的反转）：<ul><li>交换并反转：<code>row[l], row[r] = 1 - row[r], 1 - row[l]</code>。<br />这一步同时完成了水平翻转（交换左右元素）和像素反转（<code>1 - x</code>）。</li><li>然后 <code>l += 1</code>，<code>r -= 1</code>。</li></ul></li><li>处理完所有行后返回 <code>image</code>。</li></ol><h4 id="思路二代码实现用户提供"><a class="markdownIt-Anchor" href="#思路二代码实现用户提供"></a> 思路二：代码实现（用户提供）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">flipAndInvertImage</span>(<span class="params">self, image: <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]]</span>) -&gt; <span class="type">List</span>[<span class="type">List</span>[<span class="built_in">int</span>]]:</span><br><span class="line">        <span class="keyword">for</span> row <span class="keyword">in</span> image:</span><br><span class="line">            l, r = <span class="number">0</span>, <span class="built_in">len</span>(row) - <span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> l &lt;= r:</span><br><span class="line">                row[l], row[r] = <span class="number">1</span> - row[r], <span class="number">1</span> - row[l]</span><br><span class="line">                l += <span class="number">1</span></span><br><span class="line">                r -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> image</span><br></pre></td></tr></table></figure><h2 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h2><ul><li>时间复杂度：两种思路均为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>m</mi><mo>×</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(m \times n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个元素访问常数次。</li><li>空间复杂度：<ul><li>思路一：每行切片产生新列表，额外 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> 空间（但只在单行内，整体为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>m</mi><mo>×</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(m \times n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> 的临时空间）。</li><li>思路二：原地双指针交换，额外空间 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，更优。</li></ul></li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>思路一代码直观，易于理解，但使用了切片创建了新行，空间稍大。思路二将水平翻转和像素反转合并到一次遍历中，使用双指针同时完成交换和取反，不仅减少了遍历次数，还节省了空间，是本题的最优解。两种方法均可通过，推荐在实际编码中使用思路二，因为它更高效。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>包含所有三种字符的子字符串数目-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/06/%E5%8C%85%E5%90%AB%E6%89%80%E6%9C%89%E4%B8%89%E7%A7%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%95%B0%E7%9B%AE/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/06/%E5%8C%85%E5%90%AB%E6%89%80%E6%9C%89%E4%B8%89%E7%A7%8D%E5%AD%97%E7%AC%A6%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%95%B0%E7%9B%AE/</id>
    <published>2026-07-05T16:00:00.000Z</published>
    <updated>2026-07-06T05:08:13.019Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1358-包含所有三种字符的子字符串数目"><a class="markdownIt-Anchor" href="#1358-包含所有三种字符的子字符串数目"></a> 1358. 包含所有三种字符的子字符串数目</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个由字符 <code>'a'</code>、<code>'b'</code>、<code>'c'</code> 组成的字符串 <code>s</code>。请返回包含至少一个 <code>'a'</code>、一个 <code>'b'</code> 和一个 <code>'c'</code> 的子字符串数量。</p><p><strong>说明</strong>：</p><ul><li>子字符串是连续的。</li><li><code>s</code> 的长度在 <code>[3, 5×10^4]</code> 之间。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;abcabc&quot;</span><br><span class="line">输出: 10</span><br><span class="line">解释: 包含 a,b,c 的子串共有 10 个。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: s = &quot;aaacb&quot;</span><br><span class="line">输出: 3</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路滑动窗口双指针"><a class="markdownIt-Anchor" href="#思路滑动窗口双指针"></a> 思路：滑动窗口（双指针）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将字符串转为字符列表（或直接迭代字符串）。</li><li>初始化左指针 <code>left = 0</code>，计数器 <code>count = &#123;'a':0, 'b':0, 'c':0&#125;</code>，答案 <code>ans = 0</code>。</li><li>遍历右指针 <code>right</code>，将当前字符 <code>s[right]</code> 加入计数。</li><li>当窗口中同时包含至少一个 <code>'a'</code>、<code>'b'</code>、<code>'c'</code> 时，说明以当前左指针为起点、右指针为终点以及更右的终点，所有子串都满足条件。此时，<code>left</code> 表示最小的合法左端点，因此以 <code>left</code> 及更小的左端点（即 0 到 left-1）作为起点的子串也都满足条件，数量为 <code>left + 1</code>（即 <code>left</code> 本身？代码中 <code>ans += left</code> 实际上累加了 <code>left</code> 的值，但通常应为 <code>left + 1</code>。需要确认代码正确性：标准做法是 <code>ans += left + 1</code>。但用户代码写的是 <code>ans += left</code>。我们需在思考中解释。）</li><li>然后移动左指针，尝试收缩窗口，并更新计数，直到窗口不再满足条件。</li><li>返回 <code>ans</code>。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">numberOfSubstrings</span>(<span class="params">self, s: <span class="built_in">str</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        st = [c <span class="keyword">for</span> c <span class="keyword">in</span> s]</span><br><span class="line">        left = <span class="number">0</span></span><br><span class="line">        ans = <span class="number">0</span></span><br><span class="line">        count = &#123;<span class="string">&quot;a&quot;</span>: <span class="number">0</span>, <span class="string">&quot;b&quot;</span>: <span class="number">0</span>, <span class="string">&quot;c&quot;</span>: <span class="number">0</span>&#125;</span><br><span class="line">        <span class="keyword">for</span> right, ch <span class="keyword">in</span> <span class="built_in">enumerate</span>(st):</span><br><span class="line">            count[ch] += <span class="number">1</span></span><br><span class="line">            <span class="keyword">while</span> count[<span class="string">&quot;a&quot;</span>] * count[<span class="string">&quot;b&quot;</span>] * count[<span class="string">&quot;c&quot;</span>] != <span class="number">0</span>:</span><br><span class="line">                ch = st[left]</span><br><span class="line">                count[ch] -= <span class="number">1</span></span><br><span class="line">                left += <span class="number">1</span></span><br><span class="line">            ans += left</span><br><span class="line">        <span class="keyword">return</span> ans</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个元素最多被左右指针各访问一次。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，使用固定大小的字典。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>该解法利用滑动窗口统计所有满足条件的子串。当窗口内包含 <code>a</code>、<code>b</code>、<code>c</code> 至少各一个时，若左指针为 <code>left</code>，则以当前右端点 <code>right</code> 为结尾的子串中，左端点可以是 <code>0</code> 到 <code>left-1</code> 中的任意一个（因为这些子串也必然包含全部三种字符，因为它们包含当前窗口的左半部分），共 <code>left</code> 个。因此将 <code>left</code> 累加到答案中，而不是 <code>left+1</code>，是因为左端点取 <code>0</code> 到 <code>left-1</code> 共 <code>left</code> 种。这正是代码 <code>ans += left</code> 的原因。在每次满足条件并收缩后，<code>left</code> 已经是第一个使得窗口不满足条件的左端点，所以以 <code>right</code> 为结尾的合法子串数为 <code>left</code>。<br /><strong>注意</strong>：若左指针从 0 开始，当第一次满足条件时，<code>left</code> 可能为 1（因为收缩一次），此时 <code>ans += 1</code>，代表以 <code>right</code> 为结尾且左端点为 0 的子串合法，数量为 1，正确。该算法是此题的高效解法，代码简洁，但在理解上需要留意累加逻辑。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>和相同的二元子数组-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/06/%E5%92%8C%E7%9B%B8%E5%90%8C%E7%9A%84%E4%BA%8C%E5%85%83%E5%AD%90%E6%95%B0%E7%BB%84/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/06/%E5%92%8C%E7%9B%B8%E5%90%8C%E7%9A%84%E4%BA%8C%E5%85%83%E5%AD%90%E6%95%B0%E7%BB%84/</id>
    <published>2026-07-05T16:00:00.000Z</published>
    <updated>2026-07-06T05:36:28.749Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="930-和相同的二元子数组"><a class="markdownIt-Anchor" href="#930-和相同的二元子数组"></a> 930. 和相同的二元子数组</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个二元数组 <code>nums</code>（只包含 0 和 1）和一个整数 <code>goal</code>，统计并返回具有和 <code>goal</code> 的<strong>非空</strong>连续子数组的个数。</p><p><strong>说明</strong>：</p><ul><li>子数组是连续的。</li><li><code>1 &lt;= nums.length &lt;= 3 * 10^4</code>。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,0,1,0,1], goal = 2</span><br><span class="line">输出: 4</span><br><span class="line">解释: 有 4 个子数组满足和等于 2。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [0,0,0,0,0], goal = 0</span><br><span class="line">输出: 15</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路滑动窗口-容斥原理基于您的代码"><a class="markdownIt-Anchor" href="#思路滑动窗口-容斥原理基于您的代码"></a> 思路：滑动窗口 + 容斥原理（基于您的代码）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>定义辅助函数 <code>getNums(k)</code>，该函数返回数组中<strong>和大于等于 k</strong> 的连续子数组个数。</li><li>在 <code>getNums(k)</code> 中：<ul><li>初始化左指针 <code>left = 0</code>，当前窗口和 <code>pre = 0</code>，答案 <code>ans = 0</code>。</li><li>遍历右指针 <code>right</code>，将 <code>nums[right]</code> 加入 <code>pre</code>。</li><li>当 <code>pre &gt;= k</code> 时，不断移动左指针（并将移出的元素从 <code>pre</code> 中减去），直到 <code>pre &lt; k</code>。</li><li>此时，对于当前右端点 <code>right</code>，所有左端点 <strong>小于</strong> <code>left</code> 的子数组（即从 <code>0</code> 到 <code>left-1</code> 开始的子数组）的和都满足 <code>&gt;= k</code>。因此，共有 <code>left</code> 个以当前 <code>right</code> 为结尾的合法子数组，将 <code>left</code> 累加到 <code>ans</code>。</li></ul></li><li>利用容斥原理：<code>sum == goal</code> 的数量 = <code>sum &gt;= goal</code> 的数量 - <code>sum &gt;= goal+1</code> 的数量。因此返回 <code>getNums(goal) - getNums(goal+1)</code>。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">numSubarraysWithSum</span>(<span class="params">self, nums: <span class="type">List</span>[<span class="built_in">int</span>], goal: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">getNums</span>(<span class="params">k: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">            ans = <span class="number">0</span></span><br><span class="line">            left = <span class="number">0</span></span><br><span class="line">            pre = <span class="number">0</span></span><br><span class="line">            <span class="keyword">for</span> right, num <span class="keyword">in</span> <span class="built_in">enumerate</span>(nums):</span><br><span class="line">                pre += num</span><br><span class="line">                <span class="keyword">while</span> left &lt;= right <span class="keyword">and</span> pre &gt;= k:</span><br><span class="line">                    pre -= nums[left]</span><br><span class="line">                    left += <span class="number">1</span></span><br><span class="line">                ans += left </span><br><span class="line">            <span class="keyword">return</span> ans</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> getNums(goal) - getNums(goal + <span class="number">1</span>)</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li><strong>时间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，每个元素最多被左右指针各访问一次（两次遍历，常数倍）。</li><li><strong>空间复杂度</strong>：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，仅使用常数个变量。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>解法非常精妙！</p><ul><li>传统的滑动窗口通常直接统计“和小于 k”或“和不超过 k”的子数组个数。而您的 <code>getNums</code> 反其道而行之，通过 <code>while pre &gt;= k</code> 收缩窗口，巧妙地统计了“和大于等于 k”的子数组个数。</li><li>关键在于 <code>ans += left</code>：当窗口和小于 <code>k</code> 时，<code>left</code> 之前的每一个位置作为起点，与当前 <code>right</code> 组成的子数组，其和都必然大于等于 <code>k</code>。这正是因为包含这些起点会使得窗口扩大，从而恢复 <code>&gt;= k</code> 的状态。</li><li>这种写法避开了处理 <code>0</code> 元素带来的复杂边界条件，是本题的一种优雅且高效的解法。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
  <entry>
    <title>统计「优美子数组」-LeetCode</title>
    <link href="https://blog.id-zyuan.xyz/2026/07/06/%E7%BB%9F%E8%AE%A1%E3%80%8C%E4%BC%98%E7%BE%8E%E5%AD%90%E6%95%B0%E7%BB%84%E3%80%8D/"/>
    <id>https://blog.id-zyuan.xyz/2026/07/06/%E7%BB%9F%E8%AE%A1%E3%80%8C%E4%BC%98%E7%BE%8E%E5%AD%90%E6%95%B0%E7%BB%84%E3%80%8D/</id>
    <published>2026-07-05T16:00:00.000Z</published>
    <updated>2026-07-06T05:52:42.512Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="\assets\css\APlayer.min.css"><script src="\assets\js\APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="1248-统计优美子数组"><a class="markdownIt-Anchor" href="#1248-统计优美子数组"></a> 1248. 统计「优美子数组」</h1><h2 id="问题描述来源于leetcode"><a class="markdownIt-Anchor" href="#问题描述来源于leetcode"></a> 🎯 问题描述（来源于LeetCode）</h2><p><strong>描述</strong>：<br />给定一个整数数组 <code>nums</code> 和一个整数 <code>k</code>。如果一个连续子数组中<strong>恰好有 <code>k</code> 个奇数数字</strong>，我们就称这个子数组为「优美子数组」。<br />请返回这个数组中「优美子数组」的数目。</p><p><strong>说明</strong>：</p><ul><li>子数组是连续的。</li><li><code>1 &lt;= nums.length &lt;= 5 * 10^4</code>。</li></ul><p><strong>示例</strong>：</p><ul><li>示例 1：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [1,1,2,1,1], k = 3</span><br><span class="line">输出: 2</span><br><span class="line">解释: 包含 3 个奇数的子数组有 [1,1,2,1] 和 [1,2,1,1]。</span><br></pre></td></tr></table></figure><ul><li>示例 2：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [2,4,6], k = 1</span><br><span class="line">输出: 0</span><br></pre></td></tr></table></figure><h2 id="解题思路"><a class="markdownIt-Anchor" href="#解题思路"></a> 💻 解题思路</h2><h3 id="思路滑动窗口-容斥原理基于您的代码"><a class="markdownIt-Anchor" href="#思路滑动窗口-容斥原理基于您的代码"></a> 思路：滑动窗口 + 容斥原理（基于您的代码）</h3><h4 id="算法步骤"><a class="markdownIt-Anchor" href="#算法步骤"></a> 算法步骤</h4><ol><li>将原数组 <code>nums</code> 转换为 0/1 数组：偶数 → 0，奇数 → 1。</li><li>定义辅助函数 <code>atLeast(x)</code>，返回原数组中<strong>奇数个数至少为 <code>x</code></strong> 的连续子数组的个数。<ul><li>在 <code>atLeast(x)</code> 中：<ul><li>使用滑动窗口维护当前窗口内奇数的个数（即 <code>pre</code>）。</li><li>遍历右端点 <code>right</code>，将 <code>nums[right]</code> 加入 <code>pre</code>。</li><li>当 <code>pre &gt;= x</code> 时，不断移动左指针 <code>left</code>（并减去移出的元素），直到 <code>pre &lt; x</code>。</li><li>此时，对于当前右端点，所有左端点 <strong>小于</strong> <code>left</code> 的子数组（即起点 0 到 <code>left-1</code>）都满足奇数个数至少为 <code>x</code>，共有 <code>left</code> 个，累加到答案 <code>ans</code>。</li></ul></li></ul></li><li>利用容斥：恰好为 <code>k</code> 个奇数的子数组数 = <code>atLeast(k) - atLeast(k+1)</code>。</li><li>返回结果。</li></ol><h4 id="代码实现您的代码"><a class="markdownIt-Anchor" href="#代码实现您的代码"></a> 代码实现（您的代码）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">List</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">numberOfSubarrays</span>(<span class="params">self, nums: <span class="type">List</span>[<span class="built_in">int</span>], k: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        <span class="comment"># 将原数组转为 0/1：奇数=1，偶数=0</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">isNum</span>(<span class="params">num</span>):</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">if</span> num % <span class="number">2</span> == <span class="number">1</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">        <span class="keyword">for</span> i, num <span class="keyword">in</span> <span class="built_in">enumerate</span>(nums):</span><br><span class="line">            nums[i] = isNum(num)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 统计奇数个数 &gt;= x 的子数组个数</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">atLeast</span>(<span class="params">x: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">            ans = <span class="number">0</span></span><br><span class="line">            left = <span class="number">0</span></span><br><span class="line">            pre = <span class="number">0</span></span><br><span class="line">            <span class="keyword">for</span> right, num <span class="keyword">in</span> <span class="built_in">enumerate</span>(nums):</span><br><span class="line">                pre += num</span><br><span class="line">                <span class="keyword">while</span> left &lt;= right <span class="keyword">and</span> pre &gt;= x:</span><br><span class="line">                    pre -= nums[left]</span><br><span class="line">                    left += <span class="number">1</span></span><br><span class="line">                ans += left</span><br><span class="line">            <span class="keyword">return</span> ans</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> atLeast(k) - atLeast(k + <span class="number">1</span>)</span><br></pre></td></tr></table></figure><h4 id="性能分析"><a class="markdownIt-Anchor" href="#性能分析"></a> 📊 性能分析</h4><ul><li>时间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，三次遍历（一次转换 + 两次 <code>atLeast</code>），但每次遍历都是线性的，总体仍为 O(n)。</li><li>空间复杂度：<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>，仅使用常数个变量（原地修改数组，不算额外空间）。</li></ul><h4 id="思考"><a class="markdownIt-Anchor" href="#思考"></a> 思考</h4><p>您的解法非常巧妙，将「恰好 k 个奇数」转化为「至少 k 个」与「至少 k+1 个」的差，完美复用了 LeetCode 930 题的滑动窗口模板。<br />关键点在于 <code>ans += left</code>：当窗口内奇数个数下降到小于 <code>x</code> 时，<code>left</code> 是第一个使得窗口不满足条件的左边界，因此所有比 <code>left</code> 小的左端点（即 <code>0</code> 到 <code>left-1</code>）与当前右端点构成的子数组都至少包含 <code>x</code> 个奇数。<br />这种写法简洁高效，无需处理双指针的复杂边界，是本题的经典解法之一。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;&#92;assets&#92;css&#92;APlayer.min.css&quot;&gt;&lt;script src=&quot;&#92;assets&#92;js&#92;APlayer.min.js&quot; cla</summary>
      
    
    
    
    <category term="作业练习" scheme="https://blog.id-zyuan.xyz/categories/%E4%BD%9C%E4%B8%9A%E7%BB%83%E4%B9%A0/"/>
    
    
    <category term="LeetCode" scheme="https://blog.id-zyuan.xyz/tags/LeetCode/"/>
    
  </entry>
  
</feed>
