算法:堆(Heap)


背景

Heap 可以用來實現優先級隊列,也可以用來做堆排序,本文簡單的做個介紹。

Heap

規則

  • 是一個完全二叉樹,隱含的意思是:他是平衡的、使用數組進行存儲也是連續的。
  • 給定的任意節點,該節點小於等於其父親節點,大於他們的孩子節點。

基礎知識

對於一個完全二叉樹,如果將其存儲到數組中,給定父節點的索引為:x,則:

  • left child's index is:2*x + 1。
  • right child's index is:2*x + 2。
  • root's index is:0.

說明:上面的公式很容易自己推到出來,有興趣的朋友可以推到一下,這樣就不用記住這個特性了。

圖示

存儲到數組的順序為:先存儲第一層,然后是第二層,直到第 N 層。

操作

添加和刪除后還必須保證 Heap 滿足規則。

添加

添加前

添加 6

先將 6 添加到完全樹的下一個節點,然后沿着祖先路徑,將其插入到合適的節點(不一定是根節點)。

代碼

 1             public void Insert(T item)
 2             {
 3                 if (this.IsFull())
 4                 {
 5                     throw new InvalidOperationException("容量已滿,不能插入!");
 6                 }
 7 
 8                 _items[_length++] = item;
 9                 this.MoveUp(_length - 1);
10             }

結果

刪除最大值

接着上面的例子執行刪除

先將刪除根節點(6),再將完全樹最后的節點(2)直接移動到根節點。 

接着將 2 向下插入到合適的節點,比如:5 > 4 && 5 > 2,因此結果是:

代碼

 1             public T Remove()
 2             {
 3                 if (this.IsEmpty())
 4                 {
 5                     throw new InvalidOperationException("容量已空,不能刪除!");
 6                 }
 7 
 8                 var result = _items[0];
 9                 _items[0] = _items[--_length];
10                
11                 this.MoveDown(0);
12 
13                 return result;
14             }

完整代碼

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 namespace DataStuctureStudy.Heaps
  8 {
  9     class HeapTest
 10     {
 11         public static void Test()
 12         {
 13             var heap = new Heap<int>(10);
 14             heap.Insert(1);
 15             heap.Insert(2);
 16             heap.Insert(3);
 17             heap.Insert(4);
 18             heap.Insert(5);
 19             heap.Insert(6);
 20             heap.Display();
 21             heap.Remove();
 22             heap.Display();
 23         }
 24 
 25         class Heap<T>
 26             where T : IComparable<T>
 27         {
 28             private T[] _items;
 29             private int _length;
 30 
 31             public Heap(int size)
 32             {
 33                 _items = new T[size];
 34             }
 35 
 36             public void Display()
 37             {
 38                 Console.WriteLine("數組表示");
 39                 Console.Write("[");
 40                 for (var i = 0; i < _items.Length; i++)
 41                 {
 42                     if (i < _length)
 43                     {
 44                         Console.Write(_items[i]);
 45                     }
 46                     else
 47                     {
 48                         Console.Write('-');
 49                     }
 50                 }
 51                 Console.WriteLine("]");
 52                 Console.WriteLine();
 53 
 54                 Console.WriteLine("樹形表示");
 55                 var row = 0;
 56                 var column = 0;
 57                 var level = (int)Math.Ceiling(Math.Log(_length + 1, 2));
 58                 var width = (int)Math.Pow(2, level);
 59                 for (var i = 0; i < _length; i++)
 60                 {
 61                     this.Display(_items[i], width, row, column);
 62 
 63                     if ((i + 1) == Math.Pow(2, row + 1) - 1)
 64                     {
 65                         row++;
 66                         column = 0;
 67                         Console.WriteLine();
 68                     }
 69                     else
 70                     {
 71                         column++;
 72                         if (i == _length - 1)
 73                         {
 74                             Console.WriteLine();
 75                         }
 76                     }
 77                 }
 78 
 79                 Console.WriteLine();
 80             }
 81 
 82             private void Display(T item, int width, int row, int column)
 83             {
 84                 var step = (int)((width * 3) / Math.Pow(2, row));
 85                 var itemLength = item.ToString().Length;
 86                 Console.Write(item.ToString().PadLeft((step + itemLength) / 2).PadRight(step));
 87             }
 88 
 89             public void Insert(T item)
 90             {
 91                 if (this.IsFull())
 92                 {
 93                     throw new InvalidOperationException("容量已滿,不能插入!");
 94                 }
 95 
 96                 _items[_length++] = item;
 97                 this.MoveUp(_length - 1);
 98             }
 99 
100             private void MoveUp(int index)
101             {
102                 var bottom = _items[index];
103                 var current = index;
104 
105                 while (current > 0)
106                 {
107                     var parent = (current - 1) / 2;
108                     if (_items[parent].CompareTo(bottom) > 0)
109                     {
110                         break;
111                     }
112 
113                     _items[current] = _items[parent];
114                     current = parent;
115                 }
116 
117                 _items[current] = bottom;
118             }
119 
120             public T Remove()
121             {
122                 if (this.IsEmpty())
123                 {
124                     throw new InvalidOperationException("容量已空,不能刪除!");
125                 }
126 
127                 var result = _items[0];
128                 _items[0] = _items[--_length];
129                
130                 this.MoveDown(0);
131 
132                 return result;
133             }
134 
135             private void MoveDown(int index)
136             {
137                 var top = _items[index];
138                 var current = index;
139 
140                 while (current < _length)
141                 {
142                     var large = 0;
143                     var left = 2 * current + 1;
144                     var right = left + 1;
145 
146                     if (left < _length && right < _length)
147                     {
148                         if (_items[left].CompareTo(_items[right]) >= 0)
149                         {
150                             large = left;
151                         }
152                         else
153                         {
154                             large = right;
155                         }
156                     }
157                     else if (left < _length)
158                     {
159                         large = left;
160                     }
161                     else
162                     {
163                         break;
164                     }
165 
166                     if (_items[large].CompareTo(top) <= 0)
167                     {
168                         break;
169                     }
170 
171                     _items[current] = _items[large];
172                     current = large;
173                 }
174 
175                 _items[current] = top;
176             }
177 
178             public bool IsFull()
179             {
180                 return _length == _items.Length;
181             }
182 
183             public bool IsEmpty()
184             {
185                 return _length == 0;
186             }
187         }
188     }
189 }

備注

下篇簡單的介紹一下堆排序。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM