二叉堆
一种特殊的二叉树,也就是堆数据结构,也叫作二叉堆。二叉堆是计算机科学中一种非常著名的数据结构,由于它能高效、快速地找出最大值和最小值,常被应用于优先队列。(完全二叉树 + 两极端)
能确定的是树的两端,最大堆能确定最大值,最小堆能确定最小值
二叉堆特性
- 它是一棵完全二叉树,表示树的每一层都有左侧和右侧子节点(除了最后一层的叶节点),并且最后一层的叶节点尽可能都是左侧子节点,这叫作结构特性。
- 二叉堆不是最小堆就是最大堆。最小堆允许你快速导出树的最小值,最大堆允许你快速导出树的最大值。所有的节点都大于等于(最大堆)或小于等于(最小堆)每个它的子节点。这叫作堆特性。
二叉堆的实现
const swap = (array, a, b) => [array[a], array[b]] = [array[b], array[a]];
/**
* 使用数组来实现二叉堆,对于给定位置 index 的节点,我们已知的有:
* 1. 它的左侧子节点的位置是 2 * index + 1(如果位置可用)
* 2. 它的右侧子节点的位置是 2 * index + 2(如果位置可用)
* 3. 它的父节点位置是 index / 2(如果位置可用)
*
* insert(value):这个方法向堆中插入一个新的值。如果插入成功,它返回 true,否则返回 false
* extract():这个方法移除最小值(最小堆)或最大值(最大堆),并返回这个值
* findMinimum():这个方法返回最小值(最小堆)或最大值(最大堆)且不会移除这个值
*
* @class MinHeap
*/
class MinHeap {
constructor(){
this.heap = [];
}
// 返回左节点值
getLeftIndex(index) {
return 2 * index + 1;
}
// 返回右节点值
getRightIndex(index) {
return 2 * index + 2;
}
// 返回父节点值
getParentIndex(index) {
if (index === 0) {
return undefined;
}
return Math.floor((index - 1) / 2);
}
// 思路:指将值插入堆的底部叶节点(数组末尾),然后往前将这个值和它的父节点进行交换,直到父节点小于这个插入的值
insert(value){
if (value != null) {
this.heap.push(value);
this.siftUp(this.heap.length - 1);
return true;
}
return false;
}
siftUp(index){
let parent = this.getParentIndex(index);
while(index > 0 && this.heap[parent] > this.heap[index]){
swap(this.heap, parent, index);
index = parent;
parent = this.getParentIndex(parent);
}
}
// 堆长度,节点个数
size() {
return this.heap.length;
}
// 是否为空
isEmpty() {
return this.size() === 0;
}
// 第一个堆
findMinimum() {
return this.isEmpty() ? undefined : this.heap[0]; // {1}
}
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 element = index;
const left = this.getLeftIndex(index);
const right = this.getRightIndex(index);
const size = this.size();
if(left < size && this.heap[element] > this.heap[left]){
element = left;
}
if(right < size && this.heap[element] > this.heap[right]){
element = right;
}
if (index !== element) {
swap(this.heap, index, element);
this.siftDown(element);
}
}
}
const heap = new MinHeap();
heap.insert(2);
heap.insert(3);
heap.insert(4);
heap.insert(5);
heap.insert(1);
console.log('Heap size: ', heap.size()); // 5
console.log('Heap is empty: ', heap.isEmpty()); // false
console.log('Heap min value: ', heap.findMinimum()); // 1
for (let i = 1; i < 10; i++) {
heap.insert(i);
}
console.log('Extract minimum: ', heap.extract()); // 1
// MaxHeap 类的和 MinHeap 类的算法一模一样。不同之处在于我们要把所有>(大于)的比较换成<(小于)的比较。
// this.heap[parent] > this.heap[index] 换成 this.heap[parent] < this.heap[index]
// this.heap[element] > this.heap[left] 换成 this.heap[element] < this.heap[left]
// this.heap[element] > this.heap[right] 换成 this.heap[element] < this.heap[right]