• 说到堆的相关知识,之前我在队列中也说过一些,而且优先队列就是使用的堆得思想,今天只是想主要说一下堆得上溢和下溢操作原理,也就是堆实现插入和删除的原理。

    一、堆的基础知识

    1、堆的定义

    堆是一种类似于树的结构的数据结构,堆有孩子节点,所有子节点均小于大于或者小于根节点。

    2、堆的分类

    最大堆:子节点值均小于根节点的值,这样的堆叫做最大堆;

    最小堆:子节点值均大于根节点的值,这样的堆叫做最小堆。

    3、堆的图示

    二、堆的上滤下滤(最小堆)

    首先说一下堆的上溢和下溢的作用,堆的上溢操作主要是用来实现插入操作,堆的下溢操做主要是用来实现删除操作。

    1、上滤

    说道上滤操作,我觉得结合堆的插入操作比较好理解。插入操作可以分为以下几种方式:

    (1)将新的节点插入最后的位置,记录当前位置为n

    (2)取插入新节点的父节点(即位置为n/2)比较值与当前新节点的值的大小,若比插入的值小,则将父节点值赋值到位置为n节点处。若比插入的值大,则将要插入的节点值,赋值给n位置的节点上,然后结束上滤。

    (3)取n/2位置的父节点,也就是(n/2)/2位置节点与插入的值比较大小,若比插入的值小,则将父节点值赋值到位置为n/2节点处。若比插入的值大,则将要插入的节点值,赋值给n/2位置的节点上,然后结束上滤。

    (4)取(n/2)/2位置的父节点,也就是((n/2)/2)/2位置节点与插入的值比较大小,若比插入的值小,则将父节点值赋值到位置为(n/2)/2节点处。若比插入的值大,则将要插入的节点值,赋值给(n/2)/2位置的节点上,然后结束上滤。

        .....一直重复上滤过程,也就父节点的值比插入的值小。

    (5)直到最后根节点,将插入的节点值赋值给根节点,然后结束上滤。

      

    例如:下图

    插入值为2的节点,操作入下:

    2、下滤

    说到下滤我觉得结合堆的删除操作一起来讲比较好理解。首先我们将删除的节点分类,无非两种一个是非叶子结点,也就是某一个子树的根,另一个为叶子结点。对于删除的是叶子结点直接删除,如果不是叶子结点则需要进行下滤操作。

    删除操作分为以下几步:

    (1)删除的节点是否是叶子元素,是叶子元素直接删除,删除的不是叶子节点,则需要进行下滤操作。

    (2)下滤操作的具体:1)先找到要删除元素的位置,并记录该元素的位置n

                                  2)获取并删除该堆的最后一个元素,思想是前面的一个元素删除了,最后的元素必定会紧凑到前面的某一位置上,先将这个值暂且填充到删除的位置上,然                                 后调整位置

                                  3)获取n位置的孩子节点中最小的值,记录该值的位置m,并且将该值与现在该位置的值比较大小,如果m位置的值比n位置值小的话,将m位置的值赋值                                   到n位置处,将n位置的值赋值 到m位置处;如果m位置的值比n位置值大的话,直接结束下溢过程。

                                  4)获取m位置的孩子节点中最小的值,记录该值的位置k,并且将该值与现在该位置的值比较大小,如果k位置的值比m位置值小的话,将k位置的值赋值                                   到m位置处,将m位置的值赋值 到k位置处;如果k位置的值比m位置值大的话,直接结束下溢过程。

                                   ......一直重复上滤过程,也就孩子节点最小值比插入的值小,结束下溢过程。

                                  5)直到最后叶节点,将插入的节点值赋值给叶节点,然后结束下滤。

    例如:

    删除值为2的节点

    三、堆的删除(最小值删除)和插入操作代码(实质就是优先队列的相关操作)

    以最小堆为例

    代码示例:(Java)

     1 public class PriorityQueue1 {
     2     private final int DEFUALTSIZE=4;//默认的堆大小+1,即2的平方
     3     private int []queue1;//默认底层的数组存储实现
     4     private int nowQueueSize=0;//记录当前堆得元素的个数,队列的元素个数
     5     //对优先队列进行相关的初始化,申请一些空间
     6     public PriorityQueue1(){
     7         queue1=new int[DEFUALTSIZE];
     8     }
     9     //给优先队列进行拓展空间存储大小
    10     public void enlargeArray(int newSize){
    11         System.out.println("优先队列进行拓展一次空间");
    12          int []mark=queue1;
    13          queue1=new int [newSize];
    14          for(int i=0;i<mark.length;i++){
    15              queue1[i]=mark[i];
    16          }
    17     }
    18     public void push(int m){
    19         //如果存储空间不够,申请新的空间
    20         if(nowQueueSize==queue1.length-1){
    21             enlargeArray(queue1.length*2);//拓展的大小是拓展了堆(树)得下一层
    22         }
    23         //上溢操作
    24         int markLocation=++nowQueueSize;
    25         for(queue1[0]=m;queue1[markLocation/2]>m;markLocation/=2){
    26             queue1[markLocation]=queue1[markLocation/2];
    27         }
    28         queue1[markLocation]=m;
    29         //测试入队列后的数组元素分布
    30         for(int i=1;i<=nowQueueSize;i++){
    31             System.out.print(queue1[i]+" ");
    32         }
    33         System.out.println();
    34         
    35     }
    36     public int pop() throws Exception{
    37         if(nowQueueSize==0){
    38             throw new Exception("优先队列已空,不能进行出队列操作!");
    39         }
    40         //优先队列的下溢操作
    41         int mark=queue1[1];
    42         for(int i=1;i<nowQueueSize;){
    43             int markMin;
    44             int markI;
    45             if(2*i>nowQueueSize-1){
    46                 queue1[i]=queue1[nowQueueSize];
    47                 break;
    48             }else if(2*i+1<=nowQueueSize-1){
    49                 markMin=queue1[2*i]>queue1[2*i+1]?queue1[2*i+1]:queue1[2*i];
    50                 markI=queue1[2*i]>queue1[2*i+1]?(2*i+1):(2*i);
    51                 if(queue1[nowQueueSize]<markMin){
    52                     queue1[i]=queue1[nowQueueSize];
    53                     break;
    54                 }else{
    55                     queue1[i]=markMin;
    56                     i=markI;                
    57                 }
    58             }else{
    59                 markMin=queue1[2*i];
    60                 markI=2*i;
    61                 if(queue1[nowQueueSize]<markMin){
    62                     queue1[i]=queue1[nowQueueSize];
    63                     break;
    64                 }else{
    65                     queue1[i]=markMin;
    66                     i=markI;
    67                 }
    68             }            
    69         }    
    70         nowQueueSize--;
    71         return mark;
    72     }
    73     public static void main(String[] args) throws Exception {
    74         PriorityQueue1 queueTest=new PriorityQueue1();
    75         queueTest.push(2);
    76         queueTest.push(4);
    77         queueTest.push(3);
    78         queueTest.push(1);
    79         queueTest.push(6);
    80         queueTest.push(5);
    81         for(int i=1;i<=6;i++){
    82             System.out.print(queueTest.pop()+" ");
    83         }
    84         System.out.println();
    85         System.out.println(queueTest.pop());
    86 
    87     }
    88 
    89 }

    运行结果:

    4 
    4 3 
    优先队列进行拓展一次空间
    2 3 4 
    2 3 4 6 
    2 3 4 6 5 
    2 3 4 5 6 
    Exception in thread "main" java.lang.Exception: 优先队列已空,不能进行出队列操作!
        at Queue.PriorityQueue1.pop(PriorityQueue1.java:40)
        at Queue.PriorityQueue1.main(PriorityQueue1.java:87)

    四、总结

    其实堆也只是一种思想,思想上和数的结构很像,但是实现上我们可以有数组和链表实现,主要是了解这种思想。

    关于的堆的合并操作,我找时间再补充吧!其实简单。

  • 相关阅读:
    socket 编程练习--UDP聊天程序
    练习 多线程
    练习二:求最大公约数
    练习一:单链表的反转,串的模式匹配
    【算法1】string 的全排列
    lw资料
    SDmenu滑动菜单获取导航连接部分代码
    FileOperate(个人资料)
    C/C++基本数据类型
    C++/MFC计算程序运行时间
  • 原文地址:https://www.cnblogs.com/xiaotiaosi/p/6941953.html
Copyright © 2020-2023  润新知