完全二叉树 和 堆
- 从第一层到倒数第二层,每一层都是满的
- 最后一层的结点是从左到右连续排列的(也就是说所有结点都集中排列在最左边)。
对于索引为 n 的结点来说:
- 索引
(n-1)/2的结点是它的父结点 - 索引
2*n+1的结点是它的左孩子结点 - 索引
2*n+2的结点是它的右孩子结点
堆是完全二叉树的一种特例:
- 大顶堆:一棵完全二叉树它每个结点的结点值都不小于其左右孩子的结点值
- 小顶堆:一棵完全二叉树它每个结点值都不大于其左右孩子的结点值
堆的基本操作:以大顶堆为例
示例:
给出一个大顶堆:[9, 8, 6, 3, 1]
1. 如何取出堆顶元素(删除操作)
2. 往堆里追加一个元素(插入操作)
取出堆顶元素:
/**
* @param {number} low 索引下界
* @param {number} high 索引上界
*/
const downHeap = (low, high) => {
// 初始化当前节点和其左节点j
let i = low, j = i * 2 + 1;
// j不超过上界重复对比+交换
while(j <= high){
// 如果右孩子比左孩子更大,则用右孩子和根节点比较
if(j + 1 < high && heap[j+1] > heap[j]){
j += 1;
}
//当前节点比孩子节点小则交换
if(heap[i] < heap[j]){
const temp = heap[j];
heap[j] = heap[i];
heap[i] = temp;
// 更新交换孩子的索引和其左节点的索引
i = j;
j = j * 2 + 1;
}else{
break;
}
}
}
规则:
用堆里的最后一个元素(数字1)替换掉堆顶元素,反复 向下对比+交换 的过程
往堆里追加一个元素:
/**
* @param {number} low 索引下界
* @param {number} high 索引上界
*/
const upHeap = (low, high) => {
// 初始化当前节点和其父节点j
let i = high, j = Math.floor((i - 1)/2);
// j不超过下界重复对比+交换
while(j >= low){
//当前节点比父节点大则交换
if(heap[i] > heap[j]){
const temp = heap[j];
heap[j] = heap[i];
heap[i] = temp;
// 更新交换父节点的索引和其父节点的父节点的索引
i = j;
j = Math.floor((i - 1)/2);
}else{
break;
}
}
}
规则:
新来的数据首先要追加到当前堆里最后一个元素的后面,然后反复 向上对比+交换 的过程
小顶堆同理,只是大小的比较不同。
“删除”就是“向下比较+交换”,而“添加”则是“向上比较+交换”