二叉堆基本数据结构
二叉堆(Binary Heap)没什么神秘,性质比二叉搜索树 BST 还简单。
其主要操作就两个,sink(下沉)和 swim(上浮),用以维护二叉堆的性质。
其主要应用有两个,首先是一种排序方法「堆排序」,第二是一种很有用的数据结构「优先级队列」。
https://zhuanlan.zhihu.com/p/113593148
堆的底层实际上是一棵完全二叉树,可以用数组实现
- 每个的节点元素值不小于其子节点 - 最大堆
- 每个的节点元素值不大于其子节点 - 最小堆

堆用一般用数组展示如下:
如果根节点在数组中的位置是1,第n个位置的子节点分别在2_n 与 2_n+1,第n个位置的双亲节点分别在⌊i /2⌋。因此,第1个位置的子节点在2和3.
如果根节点在数组中的位置是0,第n个位置的子节点分别在2_n+1与2_n+2,第n个位置的双亲节点分别在⌊(i-1) /2⌋。因此,第0个位置的子节点在1和2.


我们以大顶堆为例实现:
插入:上移
在数组的最末尾插入新节点,然后自下而上地调整子节点与父节点的位置:比较当前结点与父节点的大小,若不满足大顶堆的性质,则交换两节点,从而使当前子树满足二叉堆的性质。时间复杂度为O(logn)。
删除:下沉
class Heap {constructor() {this.data = [];}/*** js中以0作为数组的索引* i的父节点为 Math.floor((i-1) / 2)* i的左子节点为 nLeftIndex = 2 * (i + 1) - 1; // 2i+1* i的右子节点为 nRightIndex = 2 * (i + 1); // 2i+2*/insert(value) {this.data.push(value);let nIndex = this.data.length - 1;let nFatherIndex = Math.floor((nIndex - 1 ) / 2);while(nFatherIndex >= 0) {if(this.data[nIndex] > this.data[nFatherIndex]) {const temp = this.data[nFatherIndex];this.data[nFatherIndex] = this.data[nIndex];this.data[nIndex] = temp;}nIndex = nFatherIndex;nFatherIndex = Math.floor((nIndex - 1 ) / 2);}}// 删除最大的节点delete() {let nIndex = 0;let nLeaf = this.data.pop();// 将最后一个子节点补充到根节点this.data[nIndex] = nLeaf;let len = this.data.length - 1;while(nIndex <=len) {let nLeftIndex = 2 * (nIndex + 1) - 1;let nRightIndex = 2 * (nIndex + 1);let nSelectIndex = nLeftIndex;// 找到最大的子节点if(nRightIndex <= len) {nSelectIndex = this.data[nLeftIndex] > this.data[nRightIndex] ? nLeftIndex : nRightIndex;}if(nSelectIndex <= len && this.data[nIndex] < this.data[nSelectIndex]) {var temp = this.data[nIndex];this.data[nIndex] = this.data[nSelectIndex];this.data[nSelectIndex] = temp;}nIndex = nSelectIndex;}}print() {console.log(this.data);}build(arr) {this.data = [];for(let i = 0; i < arr.length; i++) {this.insert(arr[i]);}}}const heap = new Heap();heap.build([1, 3, 4, 2]);heap.print(); // [ 4, 2, 3, 1 ]heap.delete();heap.print(); // [ 3, 2, 1 ]
练习
215. 数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
var findKthLargest = function (nums, k) {
let minHeap = new MinHeap()
for (let i = 0; i < nums.length; i++) {
if (minHeap.size() < k) minHeap.push(nums[i])
else if (minHeap.top() < nums[i]) {
minHeap.pop()
minHeap.push(nums[i])
}
}
return minHeap.top()
};
参考
二叉堆:https://labuladong.github.io/algo/2/18/45/
https://zhuanlan.zhihu.com/p/113593148
