是什么
要封装这两个数据结构,首先要搞明白他们是什么,有什么特点
不管是最小堆还是最大堆,他们都是二叉树的衍生,在二叉树之上
他们在结构上要求每组节点都必须完整,即父节点必须存在左右两个子节点,除了最后一组,即最下层的最右侧的一组节点,可以只存在左节点
,并且必须是左节点
数据上,最大堆要求父节点的key一定要大于等于子节点,最小堆则相反

最大堆和最小堆广泛运用于堆排序算法中,在求最大最小值时,效率比较高
具体封装---最小堆
之前我封装二叉搜索树时,使用的是链表形式的主体,通过left和right指针来查询子节点, 现在我们换一种方式,用数组来存储最小最大堆
我们给每个节点按从左到右,从上到下
的顺序排序,就可以得到一个一维的数组
,且每个节点的角标,都有规律可循 ,既然如此,能否将整个树结构转化为一维的数组,通过函数查询每个节点的子节点
,最重要的是在插入和删除时,js的数组可以直接用index查找到对应元素,这完美满足的我们的需求!
获取节点角标
首先,我们封装最重要的基础方法,用来查询节点的父 / 左 / 右节点
左节点 = 当前节点角标 * 2 + 1 右节点 = 当前节点角标 * 2 + 2 父节点 = (当前节点角标 + 1) / 2

javascript
class MinHeep {
constructor() {
this.heep = []
}
getLeftIndex(index) {
return (index * 2) + 1
}
getRightIndex(index) {
return (index * 2) + 2
}
getParentIndex(index) {
return Matn.floor((index - 1) / 2)
}
}
封装简单的基本方法
封一下size和isEmpty,方便使用,顺便假装一下私有属性
javascript
isEmpty() {
return this.heep.length === 0
}
size() {
return this.heep.length
}
封装插入方法
在插入最小堆时,我们需要明确,怎么样插入才能保证最小堆的结构和数据性质保持不变呢?我的方法是,通过将节点插入到数组的末尾
,即最后一个子节点的位置,在对比它的父节点,如果父节点大于插入节点的值,则将两者交换位置
kotlin
insert(item) {
if(!item && item !== 0) return
// 将插入值放到最后
this.heep.push(item)
let index = this.heep.length - 1
this.shiftUP(index)
}
shiftUP(index) {
let parentIndex = this.getParentIndex(index)
// 当插入元素所在角标大于0且其值小于它父节点的值时,交换他两的位置
while(index > 0 && this.heep[index] < this.heep[parentIndex]) {
this.swap(this.heep, index, parentIndex)
index = parentIndex
parentIndex = this.getParentIndex(index)
}
}
swap(arr, a, b) {
let temp = arr[a]
arr[a] = arr[b]
arr[b] = temp
}
封装弹出方法
封装弹出方法,即将数组的第一个元素,堆的根节点删除,并返回,最后对堆进行重新排序
我选择的排序方法是,将最后一个节点放在根节点上,然后向下查询,拿到该节点 / 左节点 / 右节点中最小的那个,如果根节点最小,则满足最小堆性质要求,结束排序,如果是左或右节点最小,则让最小的与该节点交换位置
,直到该节点最小或者该节点的子节点为空
kotlin
extract() {
if(this.isEmpty()) return
if(this.size() === 1) return this.heep.pop()
else {
const m = this.heep[0]
this.heep[0] = this.heep.pop()
this.shiftDown(0)
return m
}
}
shiftDown(index) {
let left = this.getLeftIndex(index)
let right = this.getRightIndex(index)
let min = index
while(min < this.size()) {
if(left < this.size() && this.heep[min] > this.heep[left]) {
min = left
}
if(right < this.size() && this.heep[min] > this.heep[right]) {
min = right
}
if(index !== min) {
this.swap(this.heep, min, index)
index = min
left = this.getLeftIndex(index)
right = this.getRightIndex(index)
} else return
}
}
具体封装---最大堆
其实只需要把之前封装最小堆的一些条件判断改变就是最大堆了,我实在懒得再写一遍,大家如果看完想试试手,可以自己封装一下
其实,如果对比是自己封装在类中的方法的话,只需要继承,然后改一下这个方法就行了
kotlin
// 把 if(a > b) 改为 if(compare(a, b) === compareValue.bigger)
const compareValue = {
less: -1,
equal: 0,
bigger: 1
}
const compare = (a, b) => {
if(a === b) return compareValue.equal
else if(a > b) return compareValue.bigger
else return compareValue.less
}
// 这样,只需要将 compare 函数改为以下即可,但这样改我觉得必须要加注释,说明大小值是相反的
const compare = (a, b) => {
if(a === b) return compareValue.equal
else if(a < b) return compareValue.bigger
else return compareValue.less
}