<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>PriorityQueue on 奇伏禾</title>
        <link>/tags/priorityqueue/</link>
        <description>Recent content in PriorityQueue on 奇伏禾</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Wed, 22 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="/tags/priorityqueue/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>堆模块 — LeetCode 热门 100 题精讲（Java 向）</title>
            <link>/p/heap-module/</link>
            <pubDate>Wed, 22 Jul 2026 00:00:00 +0000</pubDate>
            <guid>/p/heap-module/</guid>
            <description>&lt;h2 id=&#34;堆的核心概念&#34;&gt;堆的核心概念&#xA;&lt;/h2&gt;&lt;p&gt;堆（Heap）是一种特殊的完全二叉树结构，通常用数组实现。Java 中的 &lt;code&gt;PriorityQueue&lt;/code&gt; 默认是小顶堆（堆顶元素最小），通过传入 &lt;code&gt;Comparator.reverseOrder()&lt;/code&gt; 可变为大顶堆。&lt;/p&gt;&#xA;&lt;p&gt;堆的核心操作时间复杂度：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;插入（offer / add）：O(log n)&lt;/li&gt;&#xA;&lt;li&gt;删除堆顶（poll）：O(log n)&lt;/li&gt;&#xA;&lt;li&gt;获取堆顶（peek）：O(1)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;堆在算法题中的核心价值在于 &lt;strong&gt;动态维护一组元素中的极值&lt;/strong&gt;，特别适合 TopK 问题（求第 K 大/小、前 K 个高频元素等）。当数据量极大且无法全量排序时，堆能以 O(n log K) 的复杂度完成筛选，远优于全排序的 O(n log n)。&lt;/p&gt;&#xA;&lt;p&gt;LeetCode 热门 100 题中，有三道题与堆密切相关：&lt;strong&gt;215. 数组中的第K个最大元素&lt;/strong&gt;（堆求 TopK）、&lt;strong&gt;347. 前 K 个高频元素&lt;/strong&gt;（哈希表统计 + 堆筛选）、&lt;strong&gt;395. 至少有K个重复字符的最长子串&lt;/strong&gt;（堆作为辅助工具，配合分治解决）。前两题是堆的经典应用，第三题展示了堆在复杂场景中的灵活运用。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;1-数组中的第k个最大元素leetcode-215&#34;&gt;1. 数组中的第K个最大元素（LeetCode 215）&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;题目&lt;/strong&gt;：给定整数数组 &lt;code&gt;nums&lt;/code&gt; 和整数 &lt;code&gt;k&lt;/code&gt;，返回数组中第 &lt;code&gt;k&lt;/code&gt; 个最大的元素。注意是排序后的第 k 个最大元素，而非第 k 个不同元素。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;暴力法&lt;/strong&gt;：对数组排序后取第 &lt;code&gt;n - k&lt;/code&gt; 个元素，时间复杂度 O(n log n)。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;堆优化思路&lt;/strong&gt;：维护一个大小为 &lt;code&gt;k&lt;/code&gt; 的小顶堆。遍历数组，将元素加入堆中，当堆大小超过 &lt;code&gt;k&lt;/code&gt; 时弹出堆顶（最小的元素）。遍历结束后，堆顶就是第 k 大的元素。因为堆中保留的是当前遍历过的所有元素中 &lt;strong&gt;最大的 k 个&lt;/strong&gt;，堆顶是这 k 个中最小的，即全局第 k 大。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;具体步骤&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;创建一个小顶堆 &lt;code&gt;PriorityQueue&amp;lt;Integer&amp;gt; minHeap = new PriorityQueue&amp;lt;&amp;gt;()&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;遍历数组每个元素 &lt;code&gt;num&lt;/code&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;minHeap.offer(num)&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;如果 &lt;code&gt;minHeap.size() &amp;gt; k&lt;/code&gt;，执行 &lt;code&gt;minHeap.poll()&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;返回 &lt;code&gt;minHeap.peek()&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;findKthLargest&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; minHeap &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; num : nums) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(num);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;peek&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;复杂度&lt;/strong&gt;：时间复杂度 O(n log k)，空间复杂度 O(k)。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：使用小顶堆而非大顶堆，因为我们要保留最大的 k 个数，堆顶是这 k 个数中最小的，便于淘汰。当 k 接近 n 时，堆方案不如直接排序，但 k 较小时优势明显。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;2-前k个高频元素leetcode-347&#34;&gt;2. 前K个高频元素（LeetCode 347）&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;题目&lt;/strong&gt;：给定整数数组 &lt;code&gt;nums&lt;/code&gt; 和整数 &lt;code&gt;k&lt;/code&gt;，返回出现频率前 &lt;code&gt;k&lt;/code&gt; 高的元素。可以按任意顺序返回答案。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;暴力法&lt;/strong&gt;：统计每个元素的频率，然后按频率排序取前 k 个，时间复杂度 O(n log n)。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;堆优化思路&lt;/strong&gt;：先用哈希表统计每个元素的频率，然后用大小为 &lt;code&gt;k&lt;/code&gt; 的小顶堆存储 &lt;code&gt;(频率, 元素)&lt;/code&gt; 对。遍历哈希表，将键值对加入堆中，当堆大小超过 &lt;code&gt;k&lt;/code&gt; 时弹出频率最小的元素。遍历结束后，堆中剩下的就是频率最高的 k 个元素。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;具体步骤&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;用 &lt;code&gt;HashMap&amp;lt;Integer, Integer&amp;gt;&lt;/code&gt; 统计每个元素的出现次数。&lt;/li&gt;&#xA;&lt;li&gt;创建小顶堆 &lt;code&gt;PriorityQueue&amp;lt;int[]&amp;gt; minHeap = new PriorityQueue&amp;lt;&amp;gt;(Comparator.comparingInt(a -&amp;gt; a[0]))&lt;/code&gt;，存储 &lt;code&gt;[频率, 元素]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;遍历哈希表的 &lt;code&gt;entrySet&lt;/code&gt;：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;minHeap.offer(new int[]{freq, val})&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;如果 &lt;code&gt;minHeap.size() &amp;gt; k&lt;/code&gt;，执行 &lt;code&gt;minHeap.poll()&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;从堆中取出所有元素，返回结果数组。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;topKFrequent&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; nums, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Map&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer, Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; freqMap &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HashMap&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; num : nums) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        freqMap.&lt;span style=&#34;color:#a6e22e&#34;&gt;put&lt;/span&gt;(num, freqMap.&lt;span style=&#34;color:#a6e22e&#34;&gt;getOrDefault&lt;/span&gt;(num, 0) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&amp;gt;&lt;/span&gt; minHeap &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(Comparator.&lt;span style=&#34;color:#a6e22e&#34;&gt;comparingInt&lt;/span&gt;(a &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; a&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (Map.&lt;span style=&#34;color:#a6e22e&#34;&gt;Entry&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Integer, Integer&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; entry : freqMap.&lt;span style=&#34;color:#a6e22e&#34;&gt;entrySet&lt;/span&gt;()) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt;{entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;getValue&lt;/span&gt;(), entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;getKey&lt;/span&gt;()});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;k&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; k; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        result&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;复杂度&lt;/strong&gt;：时间复杂度 O(n log k)，空间复杂度 O(n)。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：堆中存储的是 &lt;code&gt;[频率, 元素]&lt;/code&gt;，比较器按频率排序。这里 &lt;code&gt;k&lt;/code&gt; 是频率最高的前 k 个元素，与第 215 题的 &lt;code&gt;k&lt;/code&gt; 含义一致，都是&amp;quot;保留最大的 k 个&amp;quot;。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;3-至少有k个重复字符的最长子串leetcode-395&#34;&gt;3. 至少有K个重复字符的最长子串（LeetCode 395）&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;题目&lt;/strong&gt;：给定字符串 &lt;code&gt;s&lt;/code&gt; 和整数 &lt;code&gt;k&lt;/code&gt;，找出 &lt;code&gt;s&lt;/code&gt; 中的最长子串，要求该子串中 &lt;strong&gt;每个字符&lt;/strong&gt; 的出现次数都不少于 &lt;code&gt;k&lt;/code&gt;。返回该子串的长度。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;暴力法&lt;/strong&gt;：枚举所有子串，统计每个字符频次并检查是否都 &amp;gt;= k，时间复杂度 O(n³) 或 O(n²)。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;堆辅助的分治思路&lt;/strong&gt;：这题最经典的解法是分治，但堆可以作为一个辅助工具来优化某些步骤。&lt;/p&gt;&#xA;&lt;p&gt;核心观察：如果某个字符 &lt;code&gt;c&lt;/code&gt; 在整个字符串中的总出现次数小于 &lt;code&gt;k&lt;/code&gt;，那么任何包含 &lt;code&gt;c&lt;/code&gt; 的子串都不可能满足条件。因此，可以 &lt;strong&gt;以 &lt;code&gt;c&lt;/code&gt; 为分割点&lt;/strong&gt; 将字符串切分成若干段，递归处理每一段。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;堆的辅助作用&lt;/strong&gt;：在分治过程中，需要快速找出当前段中所有出现次数 &amp;lt; k 的字符作为分割点。可以用一个最小堆存储 &lt;code&gt;(出现次数, 字符)&lt;/code&gt;，堆顶就是出现次数最少的字符，如果堆顶次数 &amp;gt;= k，则当前段已经满足条件，直接返回段长；否则弹出该字符作为分割点进行递归。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;具体步骤&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;统计当前段 &lt;code&gt;[left, right]&lt;/code&gt; 内每个字符的出现次数。&lt;/li&gt;&#xA;&lt;li&gt;将出现次数 &amp;lt; k 的字符放入小顶堆（按次数排序）。&lt;/li&gt;&#xA;&lt;li&gt;如果堆为空，说明当前段所有字符出现次数都 &amp;gt;= k，返回段长 &lt;code&gt;right - left + 1&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;否则，取出堆顶字符 &lt;code&gt;splitChar&lt;/code&gt;，以该字符为分割点，将段分成若干子段，递归处理每个子段，取最大值。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;longestSubstring&lt;/span&gt;(String s, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; dfs(s, 0, s.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1, k);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dfs&lt;/span&gt;(String s, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; left, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; right, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (left &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; right) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; 0;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 统计当前段频次&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; freq &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;26&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; right; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        freq&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;s.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(i) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]++&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 小顶堆存储出现次数 &amp;lt; k 的字符&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&amp;gt;&lt;/span&gt; minHeap &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; PriorityQueue&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;(Comparator.&lt;span style=&#34;color:#a6e22e&#34;&gt;comparingInt&lt;/span&gt;(a &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; a&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; 26; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (freq&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; 0 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; freq&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; k) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;offer&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt;{i, freq&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;i&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;isEmpty&lt;/span&gt;()) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; right &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; left &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; splitChar &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; minHeap.&lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; maxLen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; left; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; right; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (s.&lt;span style=&#34;color:#a6e22e&#34;&gt;charAt&lt;/span&gt;(i) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; splitChar) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            maxLen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(maxLen, dfs(s, start, i &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; 1, k));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; 1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    maxLen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Math.&lt;span style=&#34;color:#a6e22e&#34;&gt;max&lt;/span&gt;(maxLen, dfs(s, start, right, k));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; maxLen;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;复杂度&lt;/strong&gt;：最坏情况下 O(n²)（如每次只分割一个字符），平均情况优于暴力。堆的引入使得寻找分割字符更高效（O(26 log 26) 常数级），但整体复杂度仍由递归决定。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：这题的堆用法与传统 TopK 不同——堆在这里是 &lt;strong&gt;辅助筛选工具&lt;/strong&gt;，用于快速定位&amp;quot;不合格&amp;quot;的字符作为分割点。如果不用堆，每次遍历 26 个字母也能达到同样效果（因为字符集只有 26 个），但堆的思路更具通用性，当字符集更大时优势更明显。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;三题对比与启发&#34;&gt;三题对比与启发&#xA;&lt;/h2&gt;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;题目&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;堆的作用&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;堆类型&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;时间复杂度&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;核心逻辑&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;第K大元素&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;保留最大的 k 个&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;小顶堆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;O(n log k)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;堆顶是第 k 大&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;前K高频元素&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;保留频次最高的 k 个&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;小顶堆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;O(n log k)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;哈希统计 + 堆筛选&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;重复字符最长子串&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;辅助寻找分割点&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;小顶堆&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;O(n²) 最坏&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;分治 + 堆定位&amp;quot;坏字符&amp;quot;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;共同本质&lt;/strong&gt;：堆在这三题中的核心价值都是 &lt;strong&gt;快速获取当前数据集中的极值&lt;/strong&gt;（最小值或频率最低值）。无论是保留 TopK 时的&amp;quot;淘汰最小值&amp;quot;，还是分治时的&amp;quot;找到最少出现的字符&amp;quot;，堆都提供了 O(log n) 的极值访问能力。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;不同侧重&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;第 215 题：堆作为 &lt;strong&gt;唯一数据结构&lt;/strong&gt;，完成 TopK 筛选。&lt;/li&gt;&#xA;&lt;li&gt;第 347 题：堆与哈希表 &lt;strong&gt;配合&lt;/strong&gt;，先统计再筛选。&lt;/li&gt;&#xA;&lt;li&gt;第 395 题：堆作为 &lt;strong&gt;辅助工具&lt;/strong&gt;，嵌入分治框架中加速决策。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;总结堆模块的核心心法&#34;&gt;总结：堆模块的核心心法&#xA;&lt;/h2&gt;&#xA;    &lt;blockquote&gt;&#xA;        &lt;p&gt;&lt;strong&gt;堆的本质是&amp;quot;动态极值容器&amp;quot;。当问题涉及&amp;quot;保留最大的 K 个&amp;quot;、&amp;ldquo;淘汰最小的&amp;rdquo;、&amp;ldquo;反复取当前最值&amp;quot;等场景时，堆往往是 O(n log K) 最优解的不二之选。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;应用堆的通用思考路径：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;识别问题是否需要 &lt;strong&gt;维护一组数据的极值&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;确定堆中存储的元素类型（数值、频率、配对对象等）。&lt;/li&gt;&#xA;&lt;li&gt;确定堆的类型——&lt;strong&gt;小顶堆&lt;/strong&gt;用于保留最大的 K 个（淘汰最小的），&lt;strong&gt;大顶堆&lt;/strong&gt;用于保留最小的 K 个（淘汰最大的）。&lt;/li&gt;&#xA;&lt;li&gt;注意堆的大小控制：通常保持堆大小不超过 K，每次插入后检查并弹出。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;    &lt;/blockquote&gt;&#xA;&lt;hr&gt;&#xA;</description>
        </item></channel>
</rss>
