应知应会 - 堆与优先队列

概述

堆是一种完全二叉树,用数组底层存储;分为:

  • 大根堆:根节点值 ≥ 左右孩子,堆顶是最大值
  • 小根堆:根节点值 ≤ 左右孩子,堆顶是最小值、

优先队列(PriorityQueue):

Java 里 PriorityQueue 本质就是小根堆;
优先队列 = 对外的逻辑结构,堆 = 底层实现结构。
核心特性:每次只能从队首取出优先级最高 / 最小 / 最大的元素。
 

详解

  • 堆是完全二叉树,可用数组顺序存储,不需要链表
  • 满足堆序性质:
    • 小根堆:父节点 ≤ 子节点,堆顶永远最小
    • 大根堆:父节点 ≥ 子节点,堆顶永远最大
     
  • 核心操作复杂度:
    • 插入、删除堆顶:O(logn)
    • 遍历找最值:比普通数组 O(n) 快很多

适用场景

  • 频繁求最大值 / 最小值
  • TopK 问题:前 K 大、前 K 小
  • 多路归并、有序合并
  • 贪心算法配套:每次选当前最优
  • 任务调度、哈夫曼编码
  • 滑动窗口最大值

 

常用解题思路

TopK 经典思路

求前 K 大 → 用小根堆,保持堆大小为 K,遍历完堆里就是最大 K 个
求前 K 小 → 用大根堆
每次取最值贪心
 
每次弹出堆顶最优值,处理后把新元素再入堆
 
双堆用法
一个大根堆存左半边小数,一个小根堆存右半边大数 → 求数据流中位数
 

常见考题

数组第k大元素

题目:给一个整数数组 nums 和整数 k,求数组中第k个最大元素。

注意:是排序后从大到小第 K 个,不是第 K 小。

分析:维护PriorityQueue,小根堆,堆顶元素是最小的。如果堆的size < k,则元素入堆。否则,检查元素和当前堆顶谁大,大的留在堆中。遍历结束后,堆顶就是第 K 大。

    public static int solve(int[] nums, int k) {
        // 默认小根堆
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for (int num : nums) {
            if (heap.size() < k) {
                heap.offer(num);
            } else if (num > heap.peek()) {
                heap.poll();
                heap.offer(num);
            }
        }
        return heap.peek();
    }

 

Top K 个高频元素

题目:给定数组,统计每个元素出现频率,返回出现频率最高的 k 个元素。

分析:用HashMap统计出现次数,用小根堆,只存 k 个元素,每次淘汰到最小的。最后堆中剩的就是。

 

 

 

 

 

 

posted @ 2026-05-13 15:26  注解  阅读(3)  评论(0)    收藏  举报