【算法与数据结构专场】二叉堆是什么鬼?

二叉堆是一种应用很广的数据结构,今天,咱们就来简单讲讲二叉堆。算法

往期回顾:数组

【算法与数据结构专场】BitMap算法基本操做代码实现bash

【算法与数据结构专场】BitMap算法介绍数据结构

什么是二叉堆?

二叉堆是一种特殊的堆。具备以下的特性:优化

  1. 具备彻底二叉树的特性。
  2. 堆中的任何一个父节点的值都大于等于它左右孩子节点的值,或者都小于等于它左右孩子节点的值。

根据第二条特性,咱们又能够把二叉堆分红两类:ui

一、最大堆:父节点的值大于等于左右孩子节点的值。spa

二、最小堆:父节点的值小于等于左右孩子节点的值。3d

咱们把二叉堆的根节点称之为堆顶。根据二叉堆的特性,堆顶要嘛是整个堆中的最大元素,要嘛是最小元素code

今天,咱们主要来说讲二叉堆的三个主要操做:cdn

  1. 插入一个节点。
  2. 删除一个节点。
  3. 构建一个二叉堆。

不过这里须要注意的是,在二叉堆这种结构中,对于删除一个节点,咱们通常删的是根节点

下面咱们以最小堆为例子,来说讲这些操做。

插入一个节点。

刚才咱们说二叉堆具备彻底二叉树的特性,所以,咱们在插入一个节点的时候,应该先保证节点插入后,它仍然是一颗彻底二叉树,而后再来进行调整,使它知足二叉堆的另外一个特性。

因此,在插入的时候,咱们把新节点插到彻底二叉树的最后一个位置。例如:

插入0。

以后咱们再来进行调整,调整的原则是:让新插入的节点与它的父节点进行比较,若是新节点小于父节点,则让新节点上浮,即和父节点交换位置。

上浮以后继续和它的父节点进行比较,直到父节点的值小于或等于该节点,才中止上浮,即插入结束。例如:

0比5小,上浮。

0比2小于,上浮。

0比1小,上浮。

已经到达堆顶了,插入结束。

删除节点。

前面说了,删除节点通常删除的是根节点。

和插入同样,因为二叉堆具备彻底二叉树的特性,由于删除时候,首先咱们是要立刻恢复它具备彻底二叉树的特性,因此咱们是采起这样的策略:把根节点删除以后,用二叉堆的最后一个元素顶替上来,而后在进行调整恢复。例如:

把0删除了以后,5替换上来。

以后再来调整,调整的规则和插入差很少相似,采起下沉的策略:让5和左右孩子节点相比较,若是左右节点有小于5的,则让那个较小的孩子代替5的位置,而后5下沉。

下沉以后,5继续和左右孩子相比,直到左右孩子都大于或等于5才结束。

如:5比1,3都大,1代替5的位置

5比4,2都大,2代替5的位置。

5已经不在具备左右孩子了,删除结束。

构建二叉堆

所谓构建,就是给你一个有n个节点的无序的彻底二叉树,而后把它构建成二叉堆。

刚才咱们在删除一个节点的时候,把最后一个元素补到根元素的位置上去,而后再让这个元素依次下沉,实际上,在这个元素尚未下沉以前,它就能够看做是一颗无序的彻底二叉树了

也就是说,要把一个无序的彻底二叉树调整为二叉堆,咱们可让全部非叶子节点依次下沉。不过下沉的顺序不是从根节点开始下沉(想一下相必你就 知道不能从根节点开始下沉),而是从下面的非叶子节点下称,在依次往上。举个例子:

对于这样一颗无序的彻底二叉树

8进行下沉。

接着,5进行下沉。

2没问题,以后让7进行下沉

调整完成,构建结束。

代码实现

不过这里须要说明的是,咱们二叉树通常是采用链表的方式来实现的,但二叉堆咱们是采用数组的方式来存储的。

若是知道了一个节点的位置,如何知道一个节点的左右孩子节点的位置呢?

这其实不难,根据彻底二叉树的特色,假如一个节点的下标为n,则能够求得它左孩子的下标为:2 * n+1;右孩子下标为:2 * n+2。

下面是构建代码的实现:

public class BinaryHeap {
    //上浮操做,对插入的节点进行上浮

    /**
     *
     * @param arr
     * @param length :表示二叉堆的长度
     */
    public static int[] upAdjust(int arr[], int length){
        //标记插入的节点
        int child = length - 1;
        //其父亲节点
        int parent = (child - 1)/2;
        //把插入的节点临时保存起来
        int temp = arr[child];

        //进行上浮
        while (child > 0 && temp < arr[parent]) {
            //其实不用进行每次都进行交换,单向赋值就能够了
            //当temp找到正确的位置以后,咱们再把temp的值赋给这个节点
            arr[child] = arr[parent];
            child = parent;
            parent = (child - 1) / 2;
        }
        //退出循环表明找到正确的位置
        arr[child] = temp;
        return arr;
    }

    /**

     */


    /**
     *  下沉操做,执行删除操做至关于把最后
     *  * 一个元素赋给根元素以后,而后对根元素执行下沉操做
     * @param arr
     * @param parent 要下沉元素的下标
     * @param length 数组长度
     */
    public static int[] downAdjust(int[] arr, int parent, int length) {
        //临时保证要下沉的元素
        int temp = arr[parent];
        //定位左孩子节点位置
        int child = 2 * parent + 1;
        //开始下沉
        while (child < length) {
            //若是右孩子节点比左孩子小,则定位到右孩子
            if (child + 1 < length && arr[child] > arr[child + 1]) {
                child++;
            }
            //若是父节点比孩子节点小或等于,则下沉结束
            if (temp <= arr[child])
                break;
            //单向赋值
            arr[parent] = arr[child];
            parent = child;
            child = 2 * parent + 1;
        }
        arr[parent] = temp;
        return arr;
    }


    /**
     * 构建操做
     *
     * @param arr
     */
    public static int[] buildHead(int[] arr,int length) {
        //从最后一个非叶子节点开始下沉
        for (int i = (length - 2) / 2; i >= 0; i--) {
            arr = downAdjust(arr, i, length);
        }
        return arr;
    }
}

复制代码

本次讲解到此结束。下篇继续讲解和堆有关的知识点。至于bitmap算法优化的那篇,会在以后给出。

获取更多原创文章,能够关注下个人公众号:苦逼的码农,我会不按期分享一些资源和软件等。后台回复礼包送你一份时下热门的资源大礼包,同时也感谢把文章介绍给更多须要的人

相关文章
相关标签/搜索