《算法与数据结构》学习笔记 4-3 在最大堆中添加元素,Shift Up

本文详细介绍了如何在最大堆中添加元素并保持堆性质的方法——ShiftUp操作。具体包括ShiftUp的基本概念、操作流程及代码实现等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《算法与数据结构》学习笔记 4-3 在最大堆中添加元素,Shift Up

这一小节,我们解决这样一个问题:向最大堆中添加一个元素,对应优先队列中”入队”这个操作,同时还要保持最大堆的性质,即根元素是堆中最大的元素,并且除了根节点以外任意一个节点不大于它的父亲节点。
我们将要介绍的这个操作叫做 Shift Up 。

什么是 Shift Up?

下面罗列了 Shift Up 这个操作的关键点:

  1. 新加的元素放在数组的末尾;
  2. 进行一系列的操作维护最大堆的定义;
  3. 新加入的元素调整元素的位置:只与父节点比较(不与兄弟孩子比较);
  4. 如果比父节点大,就交换位置,否则,可以停止了,这个元素就放在当前位置。

为什么要在数组的末尾添加一个元素呢?可不可以在开头、中间?

首先,在开始或者中间插入元素,要把后面的元素后移,数组开头的元素具有特殊性质,相比起来,放在末尾维护堆的性质平均开销较少。所以在数组的末尾加入元素是最自然的想法。
如果在数组末尾添加元素,时间复杂度是 O(1)。但是在数组的末尾添加了一个元素,此时的数组就不满足堆的定义和性质,我们须要进行一系列的操作,去维护堆的定义和性质。

如何维护堆的定义和性质:通过 Shift Up 把新添加的元素放置到合适的位置

Shift Up 操作描述如下:
在数组的最后位置添加一个元素,新加入的元素只和父节点比较大小(无须和它的兄弟节点比较),只要比父节点大(严格大于),就往上走,否则停止,这个新添加的元素就放置在合适的位置,同时也调整了部分元素的位置。循环这样做,这样的过程叫做 Shift Up。

Shift Up 代码实现

代码实现:

/**
 * 在堆的尾部增加一个元素,将这个元素执行 shift up 操作,保持最大堆的性质
 *
 * @param element
 * @return
 */
public void insertElement(int element) {
    //  首先确保还有位置能够放添加进来的元素
    assert count + 1 <= capacity;
    this.count = this.count + 1;
    data[count] = element;
    shiftUp(count);
}

/**
 * 将索引为 k 的元素逐渐上移,直到满足最大堆的定义
 * 还可以优化,把多次的交换工作变成多次的赋值
 * 对索引是 h 的元素执行 shiftUp 操作
 *
 * @param h
 */
private void shiftUp(int h) {
    // 当 h = 1 的时候,元素已经在堆顶,shiftUp 操作没有意义
    // data[h / 2] 表示的是 data[h] 这个元素的父亲节点
    while (h > 1 && data[h / 2] < data[h]) { // h>1 别忘了
        swap(data, h / 2, h);
        // h = h/2 ,把当前考查的索引变成父亲节点的索引
        h /= 2;
    }
}

特别说明:
1. 有索引就必须要考虑索引的边界问题,就是这里说的 h>1,因为当 h = 1 的时候,元素已经在堆顶, Shift Up 操作没有意义;

  1. 像插入排序一样,我们先给出一个 Shift Up 等价的写法,把 while 循环里面的判断写到循环体中如判断,我个人认为下面的代码逻辑更加清晰一点,更能表达 Shift Up 的语义,更突出了”当当前元素小于等于其父亲节点的时候,终止循环”这个逻辑。
private void shiftUp(int h) {
    while (h > 1) { // h>1 别忘了
        if(data[h / 2] < data[h]){
            swap(data, h / 2, h);
            h /= 2;
        }else{
            break;
        }
    }
}

Shift Up 的过程可以转化为多次赋值

我们理解下面这种写法,可以通过插入排序的优化那一部分介绍的内容来理解。过程其实很简单,先把这个要 Shift Up 的元素存起来,只要遇到父亲元素比这个存起来的元素小,就把父亲节点的元素覆盖到当前元素,等到条件不满足的时候,就把存起来的元素值赋给当前元素即可。

Java 代码实现:

private void shiftUp(int h) {
    int temp = data[h];
    while (h > 1) {
        if (data[h / 2] < temp) {
            data[h] = data[h / 2];
            h /= 2;
        } else {
            break;
        }
    }
    data[h] = temp;
}

下一节,我么介绍什么是从最大堆中取出一个元素,以及如何取出这个元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值