一、什么是二叉堆(最小堆)?
二叉堆是一种完全二叉树,其中每个父节点的值都小于或等于其子节点的值。最小堆是二叉堆的一种,它的特点是每个节点的值都小于或等于其子节点的值,因此根节点的值是整个堆中的最小值。ps:最大堆正好相反,根节点为最大值。
二叉堆的特点
- 完全二叉树:二叉堆是一棵完全二叉树,意味着除了最后一层外,每一层的节点都被填满了,并且最后一层的节点从左到右依次排列。
- 堆的性质:对于最小堆,每个节点的值都不大于其任何一个子节点的值。因此,堆的根节点是最小值。
- 二叉树结构:每个节点至多有两个子节点,左子节点和右子节点。
为什么使用二叉堆?
二叉堆有着高效的插入、删除和访问最小(或最大)元素的特点。特别是在实现优先级队列时,最小堆提供了O(log n)的时间复杂度来插入和删除元素,因此它在处理动态优先级队列时非常有用。
二、二叉堆的基本操作
1. 插入操作
插入元素时,首先将元素插入到堆的末尾,然后通过上浮操作调整堆的结构,以保持堆的性质。上浮操作会将插入的元素与父节点进行比较,如果插入的元素小于父节点,则交换位置,直到堆的性质得到恢复。
2. 删除最小元素(根节点)
删除根节点(即最小值)时,我们首先将堆的最后一个元素移到根节点的位置,然后通过下沉操作调整堆的结构。下沉操作会将新的根节点与其较小的子节点交换,直到堆的性质恢复。
3. 获取最小元素
由于最小堆的根节点始终是最小值,获取最小元素的操作是O(1)的时间复杂度。
三、最小堆的JavaScript实现
下面我们将实现一个最小堆,支持插入、删除最小元素、获取最小元素和打印堆等操作。
class MinHeap {
constructor() {
this.heap = []; // 存储堆的数组
}
// 获取父节点的索引
parent(index) {
return Math.floor((index - 1) / 2);
}
// 获取左子节点的索引
leftChild(index) {
return 2 * index + 1;
}
// 获取右子节点的索引
rightChild(index) {
return 2 * index + 2;
}
// 判断一个节点是否有左子节点
hasLeftChild(index) {
return this.leftChild(index) < this.heap.length;
}
// 判断一个节点是否有右子节点
hasRightChild(index) {
return this.rightChild(index) < this.heap.length;
}
// 交换堆中两个元素的位置
swap(index1, index2) {
const temp = this.heap[index1];
this.heap[index1] = this.heap[index2];
this.heap[index2] = temp;
}
// 上浮操作:将元素上浮到合适的位置
bubbleUp(index) {
while (index > 0 && this.heap[this.parent(index)] > this.heap[index]) {
this.swap(index, this.parent(index));
index = this.parent(index);
}
}
// 下沉操作:将元素下沉到合适的位置
heapifyDown(index) {
let smallest = index;
if (this.hasLeftChild(index) && this.heap[this.leftChild(index)] < this.heap[smallest]) {
smallest = this.leftChild(index);
}
if (this.hasRightChild(index) && this.heap[this.rightChild(index)] < this.heap[smallest]) {
smallest = this.rightChild(index);
}
if (smallest !== index) {
this.swap(index, smallest);
this.heapifyDown(smallest);
}
}
// 插入操作
insert(value) {
this.heap.push(value); // 将元素插入到堆的末尾
this.bubbleUp(this.heap.length - 1); // 上浮调整堆
}
// 删除最小元素(根节点)
removeMin() {
if (this.heap.length === 0) return null;
const min = this.heap[0]; // 获取最小元素
this.heap[0] = this.heap[this.heap.length - 1]; // 将堆的最后一个元素放到根节点
this.heap.pop(); // 删除最后一个元素
this.heapifyDown(0); // 下沉调整堆
return min;
}
// 获取最小元素(根节点)
getMin() {
return this.heap.length > 0 ? this.heap[0] : null;
}
// 打印堆的元素
printHeap() {
console.log(this.heap);
}
}
// 示例:使用MinHeap实现优先级队列
const minHeap = new MinHeap();
minHeap.insert(10);
minHeap.insert(5);
minHeap.insert(8);
minHeap.insert(3);
minHeap.insert(7);
minHeap.insert(2);
console.log('最小元素:', minHeap.getMin()); // 输出: 2
minHeap.printHeap(); // 输出堆的结构 [ 2, 5, 3, 10, 7, 8 ]
console.log('删除最小元素:', minHeap.removeMin()); // 输出: 2
minHeap.printHeap(); // 输出堆的结构 [ 3, 5, 8, 10, 7 ]
console.log('删除最小元素:', minHeap.removeMin()); // 输出: 3
minHeap.printHeap(); // 输出堆的结构 [ 5, 7, 8, 10 ]