堆 (heap) 与优先队列
基础知识
- 完全二叉树的回顾
- 父节点的编号存在可计算的关系,因此不需要边存储的信息
- 可以用连续空间存储
- 堆的概念
- 一种基于完全二叉树的结构
- 大顶堆和小顶堆
- 大顶堆:任意三元组之间,父节点都大于子节点
- 小顶堆:任意三元组之间,父节点都小于子节点
- 堆顶元素为最大值或最小值
堆的基本操作(手撕小顶堆)
class Heap {
constructor(data) {
this.data = data;
this.compartor = (a, b) => a - b;
this.heapify();
}
// 堆的长度
size() {
return this.data.length;
}
// 初始化小顶堆
heapify() {
if (this.size() < 2) return;
for (let i = 1; i < this.size(); i++) {
this.bubbleUp(i);
}
}
// 两两交换
swap(i, j) {
if (i === j) return;
[this.data[i], this.data[j]] = [this.data[j], this.data[i]];
}
// 获取堆顶元素
peek() {
if (!this.size()) return null;
return this.data[0];
}
// 添加元素
offer(val) {
this.data.push(val);
this.bubbleUp(this.size() - 1);
}
// 删除元素
poll() {
let res = this.data[0];
this.data[0] = this.data.pop();
if (this.size()) {
this.bubbleDown(0);
}
return res;
}
// 上浮(向上调整)
bubbleUp(index) {
while (index) {
let prentIndex = (index - 1) >> 1;
if (this.compartor(this.data[index], this.data[prentIndex]) < 0) {
this.swap(index, prentIndex);
index = prentIndex;
} else {
break;
}
}
}
// 下沉(向下调整)
bubbleDown(index) {
// 获取最大下标,保证不会交换出界
let lastIndex = this.size() - 1;
while (index < lastIndex) {
let leftIndex = index * 2 + 1;
let rightIndex = index * 2 + 2;
let findIndex = index;
if (leftIndex <= lastIndex && this.compartor(this.data[leftIndex], this.data[findIndex]) < 0) {
findIndex = leftIndex;
}
if (rightIndex <= lastIndex && this.compartor(this.data[rightIndex], this.data[findIndex]) < 0) {
findIndex = rightIndex;
}
if (index !== findIndex) {
this.swap(index, findIndex);
index = findIndex;
} else {
break;
}
}
}
}
let arr = [8, 7, 6];
let H = new Heap(arr);
H.offer(9);
H.poll();
console.log(H.data);
经典面试题