(算法)堆结构

81 阅读2分钟

完全二叉树 和 堆

  • 从第一层到倒数第二层,每一层都是满的
  • 最后一层的结点是从左到右连续排列的(也就是说所有结点都集中排列在最左边)。

对于索引为 n 的结点来说:

  1. 索引 (n-1)/2 的结点是它的父结点
  2. 索引 2*n+1 的结点是它的左孩子结点
  3. 索引 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;
        }
    }
}

规则:

新来的数据首先要追加到当前堆里最后一个元素的后面,然后反复 向上对比+交换 的过程

小顶堆同理,只是大小的比较不同。

删除”就是“向下比较+交换”,而“添加”则是“向上比较+交换