这是我参与8月更文挑战的第八天,活动详情查看:8月更文挑战”
今天这一章就来介绍一下堆这种数据结构。
堆
-
堆是一种特殊的
完全二叉树。 -
所有的节点都大于等于(最大堆)或小于等于(最小堆)它的子节点。
JS中的堆
JS中通常用数组表示堆。
之前讲到的二叉树都是用 Object 表示的,这里怎么可以用数组呢?
如图:
我们可以把广度优先遍历的顺序作为数组下标,遍历的值作为数组的值,这样我们就可以表示一个堆了。
-
左侧子节点的位置是 2 * index + 1 。
-
右侧子节点的位置是 2 * index + 2 。
-
父节点位置是 (index - 1)/ 2 。
堆的应用
-
堆能高效、快速地找出最大值和最小值,时间复杂度:O(1)。
-
找出第 K 个最大(小)元素。
如何找到第 K 个最大元素呢?
-
构建一个最小堆,并将元素依次插入堆中。
-
当堆的容量超过 K, 就删除堆顶。
-
插入结束后,堆顶就是第 K 个最大元素。
首先我们先实现一个最小堆类
声明
- 在类里,声明一个数组,用来装元素。
class MinHeap {
constructor() {
this.heap = []
}
}
实现主要方法
主要方法:插入、删除堆顶、获取堆顶、获取堆大小。
插入
-
将值插入堆的底部,即数组的尾部。
-
然后上移:将这个值和它的父节点进行交换,直到父节点小于等于这个插入的值。
-
大小为 K 的堆中插入元素的时间复杂度为
O(logk)
class MinHeap {
constructor() {
this.heap = []
}
swap(i1, i2) {
const temp = this.heap[i1]
this.heap[i1] = this.heap[i2]
this.heap[i2] = temp
}
getParentIndex(i) {
// 如下写,显得比较臃肿
// Math.floor((i - 1) / 2)
// 可以通过二进制向前移一位的操作
// 实现除2取整的
return (i - 1) >> 1
}
shiftUp(index) {
if (index === 0) return
const parentIndex = this.getParentIndex(index)
if (this.heap[parentIndex] > this.heap[index]) {
this.swap(parentIndex, index)
this.shiftUp(parentIndex)
}
}
insert(value) {
this.heap.push(value)
this.shiftUp(this.heap.length - 1)
}
}
const h = new MinHeap()
h.insert(3)
h.insert(2)
h.insert(1)
console.log(h);
// MinHeap { heap: [ 1, 3, 2 ] }
- 虽然不能像数组那样呈递增,但是可以保证堆顶一定比后面的节点小。
删除堆顶
-
用数组尾部元素替换堆顶(直接删除堆顶会破坏堆结构)。
-
然后下移: 将新堆顶和它的子节点进行交换,直到子节点大于等于这个新堆顶。
-
大小为 K 的堆中删除堆顶的时间复杂度为 O(logk)。
class MinHeap {
constructor() {
this.heap = []
}
swap(i1, i2) {
const temp = this.heap[i1]
this.heap[i1] = this.heap[i2]
this.heap[i2] = temp
}
getLeftIndex(i) {
return i * 2 + 1
}
getRightIndex(i) {
return i * 2 + 2
}
shiftDown(index) {
const leftIndex = this.getLeftIndex(index)
const rightIndex = this.getRightIndex(index)
if (this.heap[leftIndex] < this.heap[index]) {
this.swap(leftIndex, rightIndex)
this.shiftDown(leftIndex)
}
if (this.heap[rightIndex] < this.heap[index]) {
this.swap(rightIndex, rightIndex)
this.shiftDown(rightIndex)
}
}
pop() {
this.heap[0] = this.heap.pop()
this.shiftDown(0)
}
}
const h = new MinHeap()
h.insert(3)
h.insert(2)
h.insert(1)
h.pop()
console.log(h)
// MinHeap { heap: [ 2, 3 ] }
获取堆顶和堆的大小
-
获取堆顶:返回数组的头部。
-
获取堆的大小:返回数组的长度。
class MinHeap {
constructor() {
this.heap = []
}
peek() {
return this.heap[0]
}
size() {
return this.heap.length
}
}
const h = new MinHeap()
h.insert(3)
h.insert(2)
h.insert(1)
console.log(h);
// 1 3
End~~~