堆
說到堆的相關知識,之前我在隊列中也說過一些,而且優先隊列就是使用的堆得思想,今天只是想主要說一下堆得上溢和下溢操作原理,也就是堆實現插入和刪除的原理。
一、堆的基礎知識
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)
四、總結
其實堆也只是一種思想,思想上和數的結構很像,但是實現上我們可以有數組和鏈表實現,主要是了解這種思想。
關于的堆的合并操作,我找時間再補充吧!其實簡單。
總結
- 上一篇: 操作系统复习提纲
- 下一篇: 计算机网络部分简答题