一、最小堆(PriorityQueue)基础操作
1. 核心作用
-
自动排序,堆顶永远是最小元素(最小堆)
-
适用场景:优先处理 "最早过期""最小" 等优先级问题(如吃苹果)
2. 必背 4 个核心方法
| 方法 | 作用 | 备注 |
| add(x) | 向堆中添加元素 | 与 offer(x) 功能完全一致,写题随便用 |
| offer(x) | 向堆中添加元素 | 同上 |
| peek() | 查看堆顶元素(不删除) | 用于 "判断堆顶是否符合条件"(如苹果是否过期) |
| poll() | 取出堆顶元素(并删除) | 用于 "移除堆顶"(如扔掉烂苹果、吃苹果) |
3. 最小堆创建语法
// 堆中存 int[](如 {过期天数, 苹果数量}),按数组第 0 位升序排序(小的在前)PriorityQueue<int[]> minHeap = new PriorityQueue b) -> a[0] - b[0]);
4. 记忆口诀
add/offer 放进去,peek 看一眼,poll 拿走它小减大 → 最小堆(a[0]-b[0])
二、题目 1:吃苹果(eatenApples)
1. 核心思想
每天按 "加新苹果 → 扔烂苹果 → 吃最快烂的苹果" 三步执行,用最小堆维护优先级。
2. 解题步骤(模板)
java
PriorityQueue[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
int day = 0, res = 0;
while (day !heap.isEmpty()) {
// 1. 加当天新苹果(如果还有苹果没长出来)
if (day < apples.length && apples[day] > 0) {
int expireDay = day + days[day]; // 过期时间 = 当前天 + 保质期
heap.offer(new int[]{expireDay, apples[day]});
}
// 2. 扔烂苹果(堆顶过期 ≤ 当前天 → 移除)
while (!heap.isEmpty() && heap.peek()[0] day) {
heap.poll();
}
// 3. 吃苹果(堆不为空则吃1个)
if (!heap.isEmpty()) {
int[] curr = heap.poll();
res++; // 吃1个,计数+1
curr[1]--; // 剩余苹果数-1
if (curr[1] > 0) { // 没吃完的放回堆
heap.offer(curr);
}
}
day++;
}
return res;
3. 关键注意点
-
先清烂苹果,再吃苹果(避免吃烂的)
-
苹果没吃完要重新放回堆(offer(curr))
-
循环条件:day < 苹果数组长度 || 堆不为空(苹果长完了但堆里还有没吃完的)
二、题目 2:设计循环队列(MyCircularQueue)
1. 核心思想
-
用数组模拟 "头尾绕圈" 的队列,遵循 先进先出(FIFO)
-
用 count 计数(最稳,避免 "牺牲一个位置" 的容量问题)
2. 成员变量(4 个)
java
private int[] data;
// 存数据的数组
private int front;
// 队头(指向第一个元素)
private int rear;
// 队尾(指向"下一个空位")private int maxSize;
// 队列最大容量
private int count;
// 当前元素个数(判断空/满的核心)
3. 7 个方法(完整模板)
(1)构造方法
java
public MyCircularQueue(int k) {
maxSize = k;
data = new int[k];
front = 0;
rear = 0;
count = 0;
}
(2)入队(enQueue)
java
public boolean enQueue(int value) {
if (isFull()) return false;
// 满了不能入队
data[rear] = value;
// 放在队尾空位
rear = (rear + 1) % maxSize;
// 队尾绕圈后移
count++;
// 元素个数+1 return true;}
(3)出队(deQueue)
java
public boolean deQueue() {
if (isEmpty()) return false;
// 空了不能出队
front = (front + 1) % maxSize;
// 队头绕圈后移(相当于删除)
count--;
// 元素个数-1
return true;
}
(4)取队头(Front)
java
public int Front() {
if (isEmpty()) return -1;
return data[front];
// front 直接指向队头元素
}
(5)取队尾(Rear)→ 最容易错
java
public int Rear() {
if (isEmpty()) return -1;
// rear 是下一个空位,真正队尾是 rear 前一位(绕圈避免负数)
int lastIndex = (rear - 1 + maxSize) % maxSize;
return data[lastIndex];
}
(6)判断为空(isEmpty)
java
public boolean isEmpty() {
return count == 0;
// count 为 0 则空
}
(7)判断为满(isFull)
java
public boolean isFull() {
return count == maxSize;
// count 等于最大容量则满}
4. 关键注意点
-
循环下标:永远用 (i + 1) % maxSize(避免越界,实现绕圈)
-
队尾计算:(rear - 1 + maxSize) % maxSize(防止 rear=0 时出现负数下标)
-
优先用 count 判断空 / 满(比 "牺牲一个位置" 更直观,不易错)
三、常见踩坑总结
1. 最小堆
-
混淆 add/offer:两者功能一致,无需纠结
-
忘记 peek() 不删除、poll() 会删除:导致逻辑漏判或误删
2. 循环队列
-
把 rear 当成队尾元素:实际是 "下一个空位",取队尾需 -1
-
成员变量写漏 / 位置错:导致 cannot find symbol 报错
-
类内部调用方法加对象名:如 this.isEmpty() 可直接写 isEmpty()
3. 通用避坑规则
-
队列永远 "尾进头出"
-
循环结构下标必用 % 容量
-
不确定空 / 满时,用 count 计数(最稳)
-
写题先判断边界(空 / 满 / 越界),再执行核心逻辑