堆是一种特殊的基于树的数据结构,其中树是[完全二叉树]。
一般来说,堆有两种类型。
在这个堆中,根节点的值必须是其所有子节点中最大的,并且其左右子树也必须执行相同的操作。最大堆中所需的比较总数取决于树的高度。完全二叉树的高度始终为logn;因此,时间复杂度也是 O(logn)。
在这个堆中,根节点的值必须是其所有子节点中最小的,并且其左右子树也必须执行相同的操作。
最小堆中所需的比较总数取决于树的高度。完全二叉树的高度始终为logn;因此,时间复杂度也是 O(logn)。
i
处的父节点与其子节点之间的关系由以下公式给出:对于从 0 开始的节点编号索引,左子节点位于索引2i+1,右子节点位于索引2i+2。min-heap和max-heap支持的操作是相同的。区别只是 min-heap 在树的根部包含最小元素,而 max-heap 在树的根部包含最大元素。
它是重新排列元素以保持堆数据结构属性的过程。当某个节点由于该节点上的某些操作而在堆中造成不平衡时,就会执行此操作。平衡树 需要O(log N) 。
heapify
操作,以便它保持堆的属性。此操作也需要O(logN) 时间。
例子:
假设初始堆(取**max-heap**)如下
8
/ \
4 5
/ \
1 2
现在如果我们将 10 插入堆中
8
/ \
4 5
/ \ /
1 2 10
heapify 操作后最终堆将如下所示
10
/ \
4 8
/ \ /
1 2 5
heapify
操作,以便它保持堆的属性。需要O(logN) 时间。
例子:
假设初始堆(取最大堆)如下:
15
/ \
5 7
/ \
2 3
现在,如果我们将堆中 15 删除,它将临时被树的叶节点替换。
3
/ \
5 7
/
2
heapify 操作后最终堆将如下所示
7
/ \
5 3
/
2
它分别找到max-heap和min-heap的最大元素或最小元素,并且我们知道最小和最大元素将始终分别是 min-heap 和 max-heap 的根节点本身。需要 O(1) 时间。
此操作分别返回并删除最大堆和最小堆中的最大元素和最小元素。也就是删除的是堆二叉树的根元素。
以下代码是max-heap的实现。
maxHeapify是负责恢复Max Heap属性的函数。它相应地排列节点i及其子树,以便维护堆属性。
JavaScript 代码示例:
class MaxHeap {
constructor(maxSize) {
// 初始化堆中的数组
this.arr = new Array(maxSize).fill(null);
// 设置最大堆的大小
this.maxSize = maxSize;
// 设置堆中元素的大小
this.heapSize = 0;
}
// 以给定的索引作为根节点,对子树进行堆化。
MaxHeapify(i) {
const l = this.lChild(i);
const r = this.rChild(i);
let largest = i;
if (l < this.heapSize && this.arr[l] > this.arr[i]) {
largest = l;
}
if (r < this.heapSize && this.arr[r] > this.arr[largest]) {
largest = r;
}
if (largest !== i) {
const temp = this.arr[i];
this.arr[i] = this.arr[largest];
this.arr[largest] = temp;
this.MaxHeapify(largest);
}
}
// 返回第i个索引元素的父级索引。
parent(i) {
return Math.floor((i - 1) / 2);
}
// 返回左侧子代的索引。
lChild(i) {
return 2 * i + 1;
}
// 返回右子节点的索引。
rChild(i) {
return 2 * i + 2;
}
// 删除根元素中包含最大元素。
removeMax() {
// 检测对数组是否为空
if (this.heapSize <= 0) {
return null;
}
if (this.heapSize === 1) {
this.heapSize -= 1;
return this.arr[0];
}
// 移除最大元素
const root = this.arr[0];
this.arr[0] = this.arr[this.heapSize - 1];
this.heapSize -= 1;
// 回复最大堆的属性
this.MaxHeapify(0);
return root;
}
// 在第 i 个元素插入新值
increaseKey(i, newVal) {
this.arr[i] = newVal;
while (i !== 0 && this.arr[this.parent(i)] < this.arr[i]) {
const temp = this.arr[i];
this.arr[i] = this.arr[this.parent(i)];
this.arr[this.parent(i)] = temp;
i = this.parent(i);
}
}
// 获取堆的最大元素
getMax() {
return this.arr[0];
}
// 获取堆的大小
curSize() {
return this.heapSize;
}
// 从指定索引删除元素
deleteKey(i) {
// 它将键值
// 增加到无穷大,然后删除
// 最大值。
this.increaseKey(i, Infinity);
this.removeMax();
}
// 在大堆中插入元素
insertKey(x) {
// 检车元素是否可以插入到堆中
if (this.heapSize === this.maxSize) {
console.log("\n 不能插入堆中 \n");
return;
}
let i = this.heapSize;
this.arr[i] = x;
// 插入元素到堆的尾部
this.heapSize += 1;
// 检查最大堆属性
// 如果违反,则恢复它.
while (i !== 0 && this.arr[this.parent(i)] < this.arr[i]) {
const temp = this.arr[i];
this.arr[i] = this.arr[this.parent(i)];
this.arr[this.parent(i)] = temp;
i = this.parent(i);
}
}
}
// 测试上述功能的驱动程序。
// 假设堆的最大大小为15。
const h = new MaxHeap(15);
// 输入数值
console.log("Entered 6 keys: 3, 10, 12, 8, 2, 14 \n");
h.insertKey(3);
console.log(h.arr)
h.insertKey(10);
console.log(h.arr);
h.insertKey(12);
console.log(h.arr)
h.insertKey(8);
console.log(h.arr);
h.insertKey(2);
console.log(h.arr);
h.insertKey(14);
console.log(h.arr);
// 打印堆的当前大小。
console.log("打印堆的当前大小" + h.curSize() + "\n");
// 打印根元素 实际上是最大的元素。
console.log("根元素" + h.getMax() + "\n");
// 删除索引2的键。
h.deleteKey(2);
// 删除后,打印堆的大小
console.log("当前堆的大小是:" + h.curSize() + "\n");
// 在堆中插入 15 和 5 两个数字
h.insertKey(15);
h.insertKey(5);
console.log("当前堆的大小是: " + h.curSize() + "\n");
console.log("当前堆中最大的元素是: " + h.getMax() + "\n");
阅读量:718
点赞量:0
收藏量:0