• 堆 续1


    --------------------siwuxie095

       

       

       

       

       

       

       

       

       

    堆的基本存储

       

       

    在堆中实现的插入操作和删除操作,都是 logN 级别的,

    显然,堆一定相应的是一个树形结构,最为经典的一种

    堆的实现叫做 二叉堆(Binary Heap)

       

       

    二叉堆对应的是二叉树,所谓二叉树,就是每一个节点,

    最多有两个子节点

       

       

    那么这个二叉树有什么特点呢? 分为两种情况:

       

    (一)最大堆

       

    1)它的任何一个节点都不大于它的父节点

    2)它必须是一棵完全二叉树

       

    满足上面两个性质的二叉树,称为 最大堆

       

    也即

       

    1)堆中某个节点的值总是不大于其父节点的值

    2)堆总是一棵完全二叉树

       

       

       

    (二)最小堆

       

    1)它的任何一个节点都不小于它的父节点

    2)它必须是一棵完全二叉树

       

    满足上面两个性质的二叉树,称为 最小堆

       

    也即

       

    1)堆中某个节点的值总是不小于其父节点的值

    2)堆总是一棵完全二叉树

       

       

       

    『所谓完全二叉树,即 除了最底层,其它层的节点

    都被元素填满,且最底层尽可能地从左到右填入』

       

       

       

       

       

    下面以最大堆为例进行介绍:

       

       

    最大堆在计算机上的一种实现:用数组存储二叉堆

       

       

       

    可能很多人都曾经实现过一棵树,觉得既然堆是一个树形结构,就应该

    采用链表的形式,设立一个节点,这个节点有左右两个指针分别指向它

    的左右孩子,这样可以实现一个堆

       

    不过,对于堆而言,有一个非常经典的实现,这种实现方式是使用数组

    来存储一个二叉堆

       

    之所以可以使用数组来存储一个二叉堆,正是因为堆是一棵完全二叉树

       

       

       

    对这颗完全二叉树,自上到下自左到右的给每一个节点标上一个序列

    号,即 依照层序自上到下、之后在每一层自左到右的标上序列号

       

    「序列号 索引」

       

       

    如果第一个节点(根节点)的索引为 1,则:

       

    对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍,

    而相应的右孩子的索引就是自身索引的二倍加一

       

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    -

    62

    41

    30

    28

    16

    22

    13

    19

    17

    15

       

    注意:索引 0 是不使用的

       

       

    通过如下公式,即可找到每个元素的双亲和左右孩子:

       

    1parent ( i ) = i / 2

    (2)left child ( i ) = 2 * i

    (3)right child ( i ) = 2 * i + 1

       

       

       

    如果第一个节点(根节点)的索引为 0,则:

       

    对于每一个节点来说,相应的左孩子的索引就是自身索引的二倍加一,

    而相应的右孩子的索引就是自身索引的二倍加二

       

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    62

    41

    30

    28

    16

    22

    13

    19

    17

    15

       

       

    通过如下公式,即可找到每个元素的双亲和左右孩子:

       

    1parent ( i ) = ( i - 1 ) / 2

    (2)left child ( i ) = 2 * i + 1

    (3)right child ( i ) = 2 * i + 2

       

       

       

       

     

    如何向最大堆中添加元素(即 插入)?

       

    这里用到了一个核心操作,通常叫做 Shift Up

       

       

    这个堆使用了数组进行表示,所以相应的,在最大堆中添加元素

    就相当于在数组的末尾添加元素,但此时的堆可能不满足最大堆

    的定义

       

    所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

       

    其实这个过程非常简单,只需要把新加入的元素调整到一个合适

    的位置,使得整个二叉树依然保持最大堆的性质

       

    具体来说:将新加入的元素和父节点的元素进行比较,如果父节

    点的元素比新加入的元素还要小,就交换位置

       

       

    新加入的元素逐渐上移的过程,就是 Shift Up 的过程

       

       

       

    如何从最大堆中取出元素(即 删除)?

       

    这里用到了一个核心操作,通常叫做 Shift Down

       

       

    如果要想从堆中取出元素,只能取出根节点的那个元素,对于最

    大堆来说,就相当于取出了优先级最大的那个元素

       

    此时,整个堆中相当于少了一个元素,怎么填补这个元素呢?

       

    只需要把整个堆的最后一个元素放到整个堆的第一个元素(根节

    点)的位置即可,但此时的堆可能不满足最大堆的定义

       

    实际上是互换了位置,之后计数器 count--,无法访问原根节点

    现在所在的位置,相当于少了一个元素,即被取出了

       

    所以要进行一系列的操作来维护堆的定义,具体应该怎么做呢?

       

    其实这个过程非常简单,只需要把此时根节点的元素调整到一个

    合适的位置,使得整个二叉树依然保持最大堆的性质

       

    具体来说:比较左右孩子的元素,谁大就和谁交换位置

       

       

    根节点的元素逐渐下移的过程,就是 Shift Down 的过程

       

       

       

       

       

       

       

    最小堆的情况类似 …

       

       

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    shiro整合springmvc
    HashMap中的位运算
    jedis的scan操作要注意cursor数据类型
    DispatcherServlet的url-pattern尽量不要配置为"/*"
    解决阿里云ECS下kubeadm部署k8s无法指定公网IP(作废)
    win10下使用mklink命令给C盘软件搬家
    Ubuntu管理软件源
    C++ 自增、自减运算符的重载和性能分析
    C++ 流插入"<<"和流提取">>"运算符的重载
    C++ 手把手教你实现可变长的数组
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/6946941.html
Copyright © 2020-2023  润新知