4-4 Shift Down如何从堆中取出一个元素(对应优先队列中出队这个操作)

Shift Down 的具体操作步骤

只能取出根节点处的元素,即第 1 个元素(索引为 1)。

步骤:将最后一个元素放到第1个元素的位置,这样做交换和移动的次数最少,并且保持了完全二叉树的性质,但是此时并不满足最大堆的性质。(想清楚为什么要这么做)

保持了完全二叉树的性质,但是此时 完全二叉树不满足最大堆的性质,我们就须要将第1 个元素逐层下移来进行判断。

具体的步骤:如果存在右孩子,就把右孩子和左孩子比较,比出最大的那个,再和自己比较,如果比自己大,就交换位置,这样的过程直到“自己比左右两个孩子都大”为止。

把当前元素和它的左右孩子,三个数进行比较,找出最大的,谁大跟谁换。

public int extractElement() {
    int num = data[1];
    assert this.count >= 0;
    swap(data, 1, count);
    this.count--;
    shiftDown(1);
    return num;
}

说明:最大堆的最后一个元素是 data[count]。

/**
 * 只要有左右孩子,左右孩子只要比自己大,就交换
 *
 * @param h
 */
private void shiftDown(int h) {
    while (2 * h <= count) { // 如果这个元素有左边的孩子
        int k = 2 * h;
        if (k + 1 <= count && data[k + 1] > data[k]) {// 如果有右边的孩子,大于左边的孩子,就好像左边的孩子不存在一样样
            k = k + 1;
        }

        if (data[h] < data[k]) {
            swap(data, h, k);
        }
        h *= 2;
    }
}

下面是我在练习的时候的一种写法,虽然没有问题,但是增大了判断的次数。

图:练习时候的一种写法

说明:在完全二叉树中,如何表示有孩子?结论:有左孩子就够了。

提示:如果我们明白这个 Shift Down 的过程的话,代码其实可以很快地写出来。

一定要注意:这里的循环条件是 2*k <= count ,等于号不能漏掉,自己手画一个完全二叉树就清楚了。

优化的思路:

逐渐下移、上移的过程可以不用交换,借用插入排序优化的思路,多次赋值,一次交换。

是不是还可以用 for 循环来做这件事情?

下面我们进行功能测试:

@Test
public void test03() {
    int capacity = 10;
    MaxHeap maxHeap = new MaxHeap(capacity);
    Random random = new Random();
    int nextInt;
    for (int i = 0; i < capacity; i++) {
        // 得到一个 [0,99] 或者说 [0,100) 区间之内的随机数
        nextInt = random.nextInt(100);
        maxHeap.insert(nextInt);
    }

    System.out.println(Arrays.toString(maxHeap.getData()));

    while (!maxHeap.isEmpty()) {
        int extractMax = maxHeap.extractMax();
        System.out.printf("%d ", extractMax);
    }
}

写到这里,我们可以直接输出来检验一下,自己写出的最大堆数据结构是否符合最大堆的性质。因为每一次从最大堆取出的总是数组中最大的元素,所以可以将最大堆用于排序,使用排序的工具检验最大堆的正确性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值