初识数据结构——优先级队列(堆!堆!堆!)_优先对列大根堆
数据结构专栏 ⬅(click)
今天就让我们来聊聊这个让无数程序员又爱又恨的数据结构——堆(Heap)。
一、优先级队列 vs 普通队列
#mermaid-svg-fnKRHWnNPAA5TtVk {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .error-icon{fill:#552222;}#mermaid-svg-fnKRHWnNPAA5TtVk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fnKRHWnNPAA5TtVk .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-fnKRHWnNPAA5TtVk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fnKRHWnNPAA5TtVk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fnKRHWnNPAA5TtVk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fnKRHWnNPAA5TtVk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fnKRHWnNPAA5TtVk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fnKRHWnNPAA5TtVk .marker.cross{stroke:#333333;}#mermaid-svg-fnKRHWnNPAA5TtVk svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fnKRHWnNPAA5TtVk .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .cluster-label text{fill:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .cluster-label span{color:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .label text,#mermaid-svg-fnKRHWnNPAA5TtVk span{fill:#333;color:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .node rect,#mermaid-svg-fnKRHWnNPAA5TtVk .node circle,#mermaid-svg-fnKRHWnNPAA5TtVk .node ellipse,#mermaid-svg-fnKRHWnNPAA5TtVk .node polygon,#mermaid-svg-fnKRHWnNPAA5TtVk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fnKRHWnNPAA5TtVk .node .label{text-align:center;}#mermaid-svg-fnKRHWnNPAA5TtVk .node.clickable{cursor:pointer;}#mermaid-svg-fnKRHWnNPAA5TtVk .arrowheadPath{fill:#333333;}#mermaid-svg-fnKRHWnNPAA5TtVk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fnKRHWnNPAA5TtVk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fnKRHWnNPAA5TtVk .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-fnKRHWnNPAA5TtVk .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-fnKRHWnNPAA5TtVk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fnKRHWnNPAA5TtVk .cluster text{fill:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk .cluster span{color:#333;}#mermaid-svg-fnKRHWnNPAA5TtVk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fnKRHWnNPAA5TtVk :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 队列 普通队列 优先级队列 FIFO 基于优先级 实现方式 堆 有序数组 无序数组
二、堆:一棵\"偏心的\"完全二叉树
堆的类型对比
// 堆的数组表示parent(i) = (i-1)/2 // 找父节点left(i) = 2*i + 1 // 左孩子right(i) = 2*i + 2 // 右孩子
#mermaid-svg-BW9lGVs5jbmCEXX0 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .error-icon{fill:#552222;}#mermaid-svg-BW9lGVs5jbmCEXX0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BW9lGVs5jbmCEXX0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .marker.cross{stroke:#333333;}#mermaid-svg-BW9lGVs5jbmCEXX0 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .cluster-label text{fill:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .cluster-label span{color:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .label text,#mermaid-svg-BW9lGVs5jbmCEXX0 span{fill:#333;color:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .node rect,#mermaid-svg-BW9lGVs5jbmCEXX0 .node circle,#mermaid-svg-BW9lGVs5jbmCEXX0 .node ellipse,#mermaid-svg-BW9lGVs5jbmCEXX0 .node polygon,#mermaid-svg-BW9lGVs5jbmCEXX0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .node .label{text-align:center;}#mermaid-svg-BW9lGVs5jbmCEXX0 .node.clickable{cursor:pointer;}#mermaid-svg-BW9lGVs5jbmCEXX0 .arrowheadPath{fill:#333333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-BW9lGVs5jbmCEXX0 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-BW9lGVs5jbmCEXX0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BW9lGVs5jbmCEXX0 .cluster text{fill:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 .cluster span{color:#333;}#mermaid-svg-BW9lGVs5jbmCEXX0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BW9lGVs5jbmCEXX0 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 堆 完全二叉树 数组存储 大根堆 小根堆 空间利用率高 索引计算快
三、堆的核心操作:上下调整
操作复杂度对比
// 向下调整示例(小根堆)void shiftDown(int[] arr, int parent, int len) { int child = 2*parent + 1; while (child < len) { // 找出较小的孩子 if (child+1 < len && arr[child+1] < arr[child]) child++; // 如果父节点已经比孩子小,调整结束 if (arr[parent] <= arr[child]) break; swap(arr, parent, child); // 交换父子 parent = child; // 继续向下调整 child = 2*parent + 1; }}
四、堆排序 vs 快速排序
#mermaid-svg-5V0xp62OAtTwO3td {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-5V0xp62OAtTwO3td .error-icon{fill:#552222;}#mermaid-svg-5V0xp62OAtTwO3td .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5V0xp62OAtTwO3td .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-5V0xp62OAtTwO3td .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5V0xp62OAtTwO3td .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5V0xp62OAtTwO3td .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5V0xp62OAtTwO3td .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5V0xp62OAtTwO3td .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5V0xp62OAtTwO3td .marker.cross{stroke:#333333;}#mermaid-svg-5V0xp62OAtTwO3td svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5V0xp62OAtTwO3td .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5V0xp62OAtTwO3td .cluster-label text{fill:#333;}#mermaid-svg-5V0xp62OAtTwO3td .cluster-label span{color:#333;}#mermaid-svg-5V0xp62OAtTwO3td .label text,#mermaid-svg-5V0xp62OAtTwO3td span{fill:#333;color:#333;}#mermaid-svg-5V0xp62OAtTwO3td .node rect,#mermaid-svg-5V0xp62OAtTwO3td .node circle,#mermaid-svg-5V0xp62OAtTwO3td .node ellipse,#mermaid-svg-5V0xp62OAtTwO3td .node polygon,#mermaid-svg-5V0xp62OAtTwO3td .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5V0xp62OAtTwO3td .node .label{text-align:center;}#mermaid-svg-5V0xp62OAtTwO3td .node.clickable{cursor:pointer;}#mermaid-svg-5V0xp62OAtTwO3td .arrowheadPath{fill:#333333;}#mermaid-svg-5V0xp62OAtTwO3td .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5V0xp62OAtTwO3td .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5V0xp62OAtTwO3td .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-5V0xp62OAtTwO3td .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-5V0xp62OAtTwO3td .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5V0xp62OAtTwO3td .cluster text{fill:#333;}#mermaid-svg-5V0xp62OAtTwO3td .cluster span{color:#333;}#mermaid-svg-5V0xp62OAtTwO3td div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5V0xp62OAtTwO3td :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 排序算法 比较排序 堆排序 快速排序 原地排序 不稳定 分治思想 平均更快
五、PriorityQueue使用指南
构造方法对比
// 大根堆实现PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a,b) -> b-a);// 自定义对象排序PriorityQueue<Student> pq = new PriorityQueue<>( (s1, s2) -> s1.score != s2.score ? s2.score - s1.score : // 分数高的在前 s1.name.compareTo(s2.name) // 分数相同按名字);
六、TopK问题的三种解法对比
#mermaid-svg-cmYpWZw0HkMW8Fz4 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .error-icon{fill:#552222;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .marker.cross{stroke:#333333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .cluster-label text{fill:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .cluster-label span{color:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .label text,#mermaid-svg-cmYpWZw0HkMW8Fz4 span{fill:#333;color:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .node rect,#mermaid-svg-cmYpWZw0HkMW8Fz4 .node circle,#mermaid-svg-cmYpWZw0HkMW8Fz4 .node ellipse,#mermaid-svg-cmYpWZw0HkMW8Fz4 .node polygon,#mermaid-svg-cmYpWZw0HkMW8Fz4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .node .label{text-align:center;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .node.clickable{cursor:pointer;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .arrowheadPath{fill:#333333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .cluster text{fill:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 .cluster span{color:#333;}#mermaid-svg-cmYpWZw0HkMW8Fz4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cmYpWZw0HkMW8Fz4 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} TopK问题 排序法 堆方法 分治法 全排序后取前K 维护大小为K的堆 类似快速选择 小根堆求最大K 大根堆求最小K
七、堆的常见面试题
1. 堆的建立过程(以小根堆为例)
//向下调整方法(复杂度logN) public static void shiftDown(int[] array, int index){ //要调整的父节点 int parent = index; //要调整的孩子节点 int child = 2*parent + 1; while (child < array.length){ //child+1其实代表的是右子树 //判断左右子树大小 if(child+1<array.length && array[child+1] < array[child]){ //左右子树对调 child = child+1; } //判断左子树和父节点的大小 if (array[child] >= array[parent]){ break; } else{ int temp = array[parent]; array[parent] = array[child]; array[child] = temp; //更新父节点和子节点的指向 parent = child; child = 2*parent +1; } } } /** *建堆操作复杂度O(n) * @param array */ public static void createHeap(int[] array){ //要先找到最后一个非叶子节点 int lastLeaf = array.length-1; int lastParent = (lastLeaf-1)/2; for (int i = lastParent; i >= 0; i--){ shiftDown(array, i); } }
2. 堆的应用场景总结
八、总结:堆的\"堆\"德
堆的优缺点分析
优点:
- 插入/删除时间复杂度稳定在O(logN)
- 获取极值(堆顶)只需O(1)
- 可以高效解决TopK问题
- 堆排序是原地排序,空间复杂度O(1)
缺点:
- 访问非堆顶元素效率低(需要遍历)
- 不是稳定排序(相同元素可能换位)
- 缓存不友好(数组跳跃访问)
#mermaid-svg-Xz1KQHZk15kuUiq8 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .error-icon{fill:#552222;}#mermaid-svg-Xz1KQHZk15kuUiq8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xz1KQHZk15kuUiq8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .marker.cross{stroke:#333333;}#mermaid-svg-Xz1KQHZk15kuUiq8 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .cluster-label text{fill:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .cluster-label span{color:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .label text,#mermaid-svg-Xz1KQHZk15kuUiq8 span{fill:#333;color:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .node rect,#mermaid-svg-Xz1KQHZk15kuUiq8 .node circle,#mermaid-svg-Xz1KQHZk15kuUiq8 .node ellipse,#mermaid-svg-Xz1KQHZk15kuUiq8 .node polygon,#mermaid-svg-Xz1KQHZk15kuUiq8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .node .label{text-align:center;}#mermaid-svg-Xz1KQHZk15kuUiq8 .node.clickable{cursor:pointer;}#mermaid-svg-Xz1KQHZk15kuUiq8 .arrowheadPath{fill:#333333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Xz1KQHZk15kuUiq8 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Xz1KQHZk15kuUiq8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xz1KQHZk15kuUiq8 .cluster text{fill:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 .cluster span{color:#333;}#mermaid-svg-Xz1KQHZk15kuUiq8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xz1KQHZk15kuUiq8 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 堆的优点 高效插入删除 快速获取极值 解决TopK 原地排序 堆的缺点 非随机访问 不稳定 缓存不友好
九、终极对比表:堆 vs 其他数据结构
最后的小幽默:
程序员的世界里:
- 当你学会堆:哇!我\"堆\"数据结构理解好深!
- 当你写堆代码:这bug怎么\"堆\"了这么多!
- 当你面试被问堆:面试官,咱们能\"堆\"心一点吗?