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

一、堆的基础知识

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)

四、总结

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

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

posted on 2017-06-04 21:13  小调~  阅读(361)  评论(0编辑  收藏  举报

导航