一些概念
- 堆:特殊的完全二叉树,具有特定性质的完全二叉树。
- 大根堆:父节点 > 子节点
- 小根堆:父节点 < 子节点
二叉堆也属于完全二叉树,所以可以用数组表示。
- 若下标从1开始,左节点为
2*i
,右节点为2*i+1
,父节点为i//2
。 - 若下标从1开始,左节点为
2*i+1
,右节点为2*i+1+2
,父节点为(i-1)//2
。
最大堆
两个重要方法,插入元素和移出元素。
- 插入元素:在堆尾插入元素,调用辅助方法,将该元素上浮到正确位置。
- 移出元素:将堆尾元素删去并替换到堆首,将该元素下沉到正确位置。
解释:
- 上浮:如果父节点更大,则替换,循环直至比父节点小。
- 下沉:如果子节点中较大的那个更小,则替换,循环直至子节点都比自身小。
实现
class MaxHeap {
constructor() {
this.heap = []
}
isEmpty() {
return this.heap.length === 0
}
size() {
return this.heap.length
}
#getParentIndex(idx) {
return Math.floor((idx-1)/2)
}
#getLeft(idx) {
return idx * 2 + 1
}
#getRight(idx) {
return idx * 2 + 2
}
// 插入
insert(v) {
this.heap.push(v)
this.#swim(this.size()-1)
}
// 删除最大值
deleteMax() {
const max = this.heap[0]
this.#swap(0, this.size() - 1) // 将根和最后一个元素交换
this.heap.pop() // 防止对象游离
this.#sink(0) // 下沉,恢复有序性
return max
}
// 第i个是否小于第j个
#compare(a, b) {
return a < b
}
// 交换
#swap(i, j) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]]
}
// 上浮
#swim(k) {
let parent = this.#getParentIndex(k)
while(k > 0 && this.#compare(this.heap[parent], this.heap[k])) {
this.#swap(parent, k)
k = parent
parent = this.#getParentIndex(k)
}
}
// 下沉
#sink(k) {
while (this.#getLeft(k) < this.size()) {
let j = this.#getLeft(k)
// j 指向子节点的较大值
if (j+1 < this.size() && this.#compare(this.heap[j], this.heap[j+1])) j++
// 如果子节点都小
if (this.#compare(this.heap[j], this.heap[k])) break
this.#swap(k, j)
k = j
}
}
}
测试
const mh = new MaxHeap()
mh.insert(20)
mh.insert(80)
mh.