一、堆和优先队列
这里说的堆是二叉堆,不是指堆内存。二叉堆分最大堆和最小堆,最大最小指堆顶元素是所有元素中的最大值最小值。二叉堆是基于完全二叉树实现的,堆顶元素就是根节点。以最小堆为例,除叶子节点外所有节点的值都小于等于其子节点。优先队列和二叉堆是什么关系?二叉堆是优先队列的一种底层实现,优先队列是提供了一系列接口enqueue、dequeue、front实现每次能获取或移除队列中的最大或最小值的队列。其中最大堆和最小堆分别对应最大优先队列和最小优先队列的实现方式。
二、用 JS 数组模拟实现一个最小优先队列
class MyPriorityQueue {
constructor() {
this._data = [];
}
enqueue(e) {
if (this._data.length === 0) {
this._data.unshift(e);
return;
}
if (e > this._data[0]) {
let tmp = this._data.shift();
this._data.unshift(e);
this._data.unshift(tmp);
} else {
this._data.unshift(e);
}
}
dequeue() {
this._data.shift();
if (this.isEmpty()) return;
let minIndex = 0;
for (let i = 1; i < this._data.length; i++) {
if (this._data[i] < this._data[minIndex]) {
minIndex = i;
}
}
let tmp = this._data[0];
this._data[0] = this._data[minIndex];
this._data[minIndex] = tmp;
}
isEmpty() {
return this._data.length === 0;
}
front() {
return this._data[0];
}
};
-
数据结构:使用普通数组存储元素,未使用堆结构
-
核心逻辑:
- enqueue(e):
- 将元素插入到数组头部(无论优先级)
- 如果新元素比当前堆顶大,则交换两者位置
- dequeue():
- 移除堆顶元素
- 遍历数组找到最小值,移到堆顶位置
- enqueue(e):
-
关键问题:
- 时间复杂度:插入和删除操作均为O(n),未利用堆的O(logn)优势
- 结构错误:未维护堆的树形结构特性,仅保证堆顶是最小值
- 逻辑冗余:插入时简单插入后再交换,删除后重新遍历找最小值
这个 JS 数组模拟实现的优先队列只保证接口调用正确,性能不好,LeetCode题目1353. 最多可以参加的会议数目 会超时,无法通过所有测试用例。
三、用二叉堆实现最小优先队列
class MinPriorityQueue {
construct