7. 二叉堆
特点
二叉堆是一种特殊的二叉树,有以下两种特点
- 结构特性:是一棵完全二叉树,树的根节点/内部节点都有左右侧子节点,且叶子节点尽量在左侧
- 堆特性:不是最大堆就是最小堆,能够快速导出最大最小值;所有节点都大于等于(最大堆)或小于等于(最小堆)每个它的子节点
- 用
Array存储数据,但可以想象成一棵树的存储形式
实现
最小堆
const { Compare, defaultCompare } = require('../utils')
class MinHeap {
constructor(compareFn = defaultCompare) {
this.compareFn = compareFn
this.heap = []
}
// 获取左侧子节点index
getLeftIndex(index) {
return index * 2 + 1
}
// 获取右侧子节点index
getRightIndex(index) {
return index * 2 + 2
}
// 获取父节点index
getParentIndex(index) {
if (index === 0) {
// 根节点
return -1
}
return Math.floor((index - 1) / 2)
}
// 插入节点
insert(value) {
if (value) {
this.heap.push(value)
// 将插入的最后一个值上移,直到父节点小于这个插入的值
this.siftUp(this.heap.length - 1)
return true
}
return false
}
// 递归上移
siftUp(index) {
let parentIndex = this.getParentIndex(index)
// 父子节点进行比较,如果父节点的值大于该值,则进行上移操作
while (
index > 0 &&
this.compareFn(this.heap[parentIndex], this.heap[index]) ===
Compare.BIGGER_THAN
) {
// 父子节点值互换
;[this.heap[parentIndex], this.heap[index]] = [
this.heap[index],
this.heap[parentIndex],
]
// 更新父子节点下标
index = parentIndex
parentIndex = this.getParentIndex(parentIndex)
}
}
size() {
return this.heap.length
}
isEmpty() {
return this.size() === 0
}
// 获取最小值
findMinimum() {
return this.isEmpty() ? undefined : this.heap[0]
}
// 移除最小值并返回
extract() {
if (this.isEmpty()) {
return undefined
}
if (this.size() === 1) {
// 只有一个值,直接返回,不需要下移操作
return this.heap.shift()
}
const removedValue = this.heap.shift()
// 从根节点开始移除
this.siftDown(0)
return removedValue
}
// 下移操作(堆化)
// 实际就是父节点和左右子节点互相比较,值互换的过程
siftDown(index) {
let currentIndex = index
const leftIndex = this.getLeftIndex(index)
const rightIndex = this.getRightIndex(index)
const size = this.size()
// 确保左子节点合法
// 比较当前节点和左侧子节点的大小
if (
leftIndex < size &&
this.compareFn(this.heap[currentIndex], this.heap[leftIndex]) ===
Compare.BIGGER_THAN
) {
currentIndex = leftIndex
}
// 继续比较右侧
if (
rightIndex < size &&
this.compareFn(this.heap[currentIndex], this.heap[rightIndex]) ===
Compare.BIGGER_THAN
) {
currentIndex = rightIndex
}
// 选择小的那一边交换
if (index !== currentIndex) {
;[this.heap[index], this.heap[currentIndex]] = [
this.heap[currentIndex],
this.heap[index],
]
// 递归下移操作
this.siftDown(currentIndex)
}
}
}
最大堆
最大堆就是最小堆的逆向运算,只需要改变compareFn的比较方式即可
const { defaultCompare } = require('../utils')
const { MinHeap } = require('./minHeap')
function reverseCompare(compareFn) {
return (a, b) => compareFn(b, a)
}
class MaxHeap extends MinHeap {
constructor(compareFn = reverseCompare(defaultCompare)) {
super(compareFn)
}
}