堆
什么是堆?
堆(Heap)是一种特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看做一棵完全二叉树的数组对象。
- 堆中某个结点的值总是不大于或不小于其父结点的值;
- 堆总是一棵完全二叉树。
- 从根节点到任意结点路径上所有的结点都是有序的
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(Ki <= K2i+1 且 Ki<=K2i+2) 或 (Ki >= K2i+1 且 Ki>=K2i+2)
堆的根节点值最大,称为最大堆(大顶堆);堆的根节点值最小,称为最小堆(小顶堆)
知道一个节点index,其左右儿子节点为:index*2+1、index*2+2;其父亲节点为:(index-1)/2 取整
堆的基本操作
-
堆的向上调整
// 向上调整,用于向堆中插入数据 sift_up(index: number) { // index当前位置索引 while (index) { // index父亲索引 let parent = Math.floor((index - 1) / 2) // 满足维护条件,则直接跳出循环 if (this.compare(parent, index)) break // 不满足维护条件,则交换,使之满足维护条件 this.swap(parent, index) // 交换后,该子树的父树发生变化,所以继续向上遍历 // 直到堆顶,或满足维护条件 index = parent } }
-
堆的向下调整
// 向下调整,用于取出堆顶数据 sift_down(index: number) { // index开始为堆顶 // index * 2 + 1左孩子,index * 2 + 2右孩子 while (index * 2 + 1 < this.size()) { let temp = index let l = index * 2 + 1 let r = index * 2 + 2 // 找到父亲节点,左右儿子中的最值(小根堆为最小值,大根堆为最大值) if (this.compare(l, temp)) temp = l if (r < this.size() && this.compare(r, temp)) temp = r // 若最值是其自身,则堆已维护好跳出循环 if (temp === index) break // 最值不是自身,则与其一个是最值的儿子交换数据 this.swap(temp, index) // 交换后,该树的子树发生变化,所以继续向下遍历 // 直到堆尾,或满足维护条件 index = temp } }
-
堆的创建
let heap = new Heap(true) Arr.forEach(item => heap.push(item))
-
堆排序
// 堆排序 小根堆升序,大根堆降序 sort() { let arr: number[] = [] while (!this.isEmpty()) { arr.push(this.pop()!) } return arr }
完整代码
class Heap {
heap: number[]
type: boolean
// 初始化
constructor(type = true) {
this.heap = []
// true为大根堆,false为小根堆
this.type = type
}
// 返回堆的长度
size() {
return this.heap.length
}
// 判断是否为空堆
isEmpty() {
return this.size() ? false : true
}
// 返回堆顶元素
top() {
if (!this.isEmpty()) throw "Heap is empty"
return this.heap[0]
}
// 添加元素
push(item: number) {
// 压入堆尾
this.heap.push(item)
// 从堆尾向堆顶维护堆
this.sift_up(this.size() - 1)
}
// 弹出元素
pop() {
// 交换堆顶与堆尾元素
this.swap(0, this.size() - 1)
// 弹出堆尾元素(即堆顶)
let item = this.heap.pop()
// 从堆顶向堆尾维护
this.sift_down(0)
return item
}
// 堆排序 小根堆升序,大根堆降序
sort() {
let arr: number[] = []
while (!this.isEmpty()) {
arr.push(this.pop()!)
}
return arr
}
// 向上调整,用于向堆中插入数据
sift_up(index: number) {
// index当前位置索引
while (index) {
// index父亲索引
let parent = Math.floor((index - 1) / 2)
// 满足维护条件,则直接跳出循环
if (this.compare(parent, index)) break
// 不满足维护条件,则交换,使之满足维护条件
this.swap(parent, index)
// 交换后,该子树的父树发生变化,所以继续向上遍历
// 直到堆顶,或满足维护条件
index = parent
}
}
// 向下调整,用于取出堆顶数据
sift_down(index: number) {
// index开始为堆顶
// index * 2 + 1左孩子,index * 2 + 2右孩子
while (index * 2 + 1 < this.size()) {
let temp = index
let l = index * 2 + 1
let r = index * 2 + 2
// 找到父亲节点,左右儿子中的最值(小根堆为最小值,大根堆为最大值)
if (this.compare(l, temp)) temp = l
if (r < this.size() && this.compare(r, temp)) temp = r
// 若最值是其自身,则堆已维护好跳出循环
if (temp === index) break
// 最值不是自身,则与其一个是最值的儿子交换数据
this.swap(temp, index)
// 交换后,该树的子树发生变化,所以继续向下遍历
// 直到堆尾,或满足维护条件
index = temp
}
}
// 比较元素大小
compare(l: number, r: number) {
// this.type为true,则为大根堆,反之则为小根堆
return this.type ? this.heap[l] > this.heap[r] : this.heap[l] < this.heap[r]
}
// 交换两元素
swap(l: number, r: number) {
[this.heap[l], this.heap[r]] = [this.heap[r], this.heap[l]]
}
}