模型
優先隊列是允許至少下列兩種操作的數據結構:插入,和刪除最小者。
插入等價入隊,刪除等價出隊。
二叉堆
二個性質:
- 結構性
- 堆序性
結構性質
堆是一顆被完全填滿的二叉樹,有可能的例外是在底層,底層上的元素從左到右填入。這樣的樹稱為完全二叉樹。
利用數組表示二叉堆
一個堆數據結構由一個數組,一個代表最大值的整數以及當前的堆大小組成。
1.struct HeapStruct; 2.typedef struct HeapStruct *PriorityQueue; 3. 4.struct HeapStruct 5.{ 6. int Capacity;//最大容量 7. int Size;//當前大小 8. int *Elements;//數組 9.};
堆序性質
使操作被快速執行的性質是堆序性。由於我們想要快速的找出最小元,因此,最小元應該在根上。
1.PriorityQueue Initialize(int MaxElements) 2.{ 3. PriorityQueue H; 4. if (MaxElements < 100) 5. { 6. Error(""); 7. } 8. H = malloc(sizeof(struct HeapStruct)); 9. 10. if (H == NULL) 11. { 12. Error(""); 13. } 14. H->Elements = malloc((MaxElements + 1) * sizeof(int)); 15. 16. if (H->Elements == NULL) 17. { 18. Error(""); 19. } 20. 21. H->Capacity = MaxElements; 22. H->Size = 0; 23. H->Elements[0] = 0; 24.}
基本堆操作
Insert(插入)
為將一個元素X插入到堆中,我們在下一個空閑位置創建一個空穴,否則該堆將不是完全樹。如果X可以放在該空穴中而並不破壞推的序,那么插入完成。否則,我們把空穴的父節點上的元素移入該空穴中,這樣空穴就朝着根的方向上行一步。繼續該過程直到X能被放入空穴中為止。這種策略叫做上濾
1.void Insert(int x, PriorityQueue H) 2.{ 3. int i; 4. if (IsFull(H)) 5. { 6. Error(""); 7. } 8. for (i = ++H->Size; H->Elements[i / 2] > x; i /= 2) 9. { 10. H->Elements[i] = H->Elements[i / 2];//移動空穴 11. H->Elements[i] = x; 12. } 13.}
如果插入的值是新的最小元素,那么它將一直被推向頂端。當i為1時,我們就需要使程序跳出循環。我們將一個很小的值放在0處,這個值必須小於或等於堆中的任何值,我們將這個值稱為標記
DeleteMin(刪除最小元)
當刪除一個最小元時,在根節點出產生一個空穴。由於現在堆少了一個元素,因此推中最后一個元素X必須移動到改堆的某個地方,如果X被放到空穴中,那么DeleteMin完成。我們把空穴的兩個兒子中較小的移入空穴中,這樣就把空穴向下推了一層。重復改步驟只到X被放入空穴中。這種策略叫做下濾
1.int DeleteMin(PriorityQueue H) 2.{ 3. int i, Child; 4. int MinElement, LastElement; 5. 6. if (IsEmpty(H)) 7. { 8. Error(""); 9. return H->Elements[0]; 10. } 11. 12. MinElement = H->Elements[1]; 13. LastElement = H->Elements[H->Size - 1]; 14. 15. for (i = 1; i * 2 < H->Size; i = Child) 16. { 17. Child = i * 2; 18. if (i!=H->Size&&H->Elements[Child+1]<H->Elements[Child]) 19. { 20. Child++; 21. } 22. if (LastElement > H->Elements[Child]) 23. { 24. 25. 26. H->Elements[i] = H->Elements[Child]; 27. } 28. else 29. break; 30. } 31. H->Elements[i] = LastElement; 32. return MinElement; 33.}
左式堆
二個性質:
- 結構性
- 堆序性
左式堆和二叉樹間唯一的區別是:左式堆不是理想平衡的,而實際上是趨向非常不平衡的。
左式堆的性質
我們把任意節點的零路徑長Npl定義為從X到一個沒有兩個兒子的節點的最短路徑的長。
性質:對於每一個節點X,左兒子的零路徑至少與右兒子的零路徑長一樣大。

左式堆的操作
對左式堆的基本操作是合並
兩個左式堆:

合並:
1.#include<stdio.h> 2. 3.struct TreeNode; 4.typedef struct TreeNode *PriorityQueue; 5. 6. 7.struct TreeNode 8.{ 9. int Element; 10. PriorityQueue Left; 11. PriorityQueue Right; 12. int Npl; 13.}; 14. 15. 16.PriorityQueue Merge(PriorityQueue H1, PriorityQueue H2) { 17. if (H1==NULL) 18. { 19. return H2; 20. } 21. if (H2==NULL) 22. { 23. return H1; 24. } 25. if (H1->Element<H2->Element) 26. { 27. return Merge1(H1, H2); 28. } 29. else 30. { 31. return Merge1(H2, H1); 32. } 33.} 34. 35.static PriorityQueue Merge1(PriorityQueue H1, PriorityQueue H2) { 36. if (H1->Left==NULL) 37. { 38. H1->Left = H2; 39. } 40. else 41. { 42. H1->Right = Merge(H1->Right, H2); 43. if (H1->Left->Npl<H1->Right->Npl)//判斷Npl 44. { 45. SwapChildren(H1);//交換子樹 46. } 47. H1->Npl = H1->Right->Npl + 1; 48. } 49. return H1; 50.} 51. 52. 53.PriorityQueue Insert1(int x, PriorityQueue H) { 54. PriorityQueue SingleNode; 55. SingleNode = malloc(sizeof(struct TreeNode)); 56. if (SingleNode==NULL) 57. { 58. FatalError("Error"); 59. } 60. else 61. { 62. SingleNode->Element = x; 63. SingleNode->Npl = 0; 64. H = Merge(SingleNode, H); 65. } 66. return H; 67.} 68. 69. 70.PriorityQueue DeleteMin1(PriorityQueue H) { 71. PriorityQueue LeftHeap, RightHeap; 72. if (IsEmpty(H)) 73. { 74. Error("Error"); 75. return H; 76. } 77. LeftHeap = H->Left; 78. RightHeap = H->Right; 79. free(H); 80. return Merge(LeftHeap, RightHeap); 81.}
斜堆
斜堆是左式堆的自調節形式。
斜堆是具有堆序的二叉樹,但是不存在對樹的結構限制。
不過,不同於左式堆,關於任意節點的零路徑長的任何信息都不保留。
與左式堆相同,斜推的基本操作也是合並操作。
對於左式堆,我們查看是否左兒子和右兒子滿足左式堆堆序性質並交換那些不滿足該性質者;對於斜堆,除了這些右路徑上所有節點的最大者不交換它們的左右兒子外,交換都是無條件的。
斜堆也也可以向左式堆那樣非遞歸的進行所有操作:合並右路徑,除最后的節點外交換右路徑上每個節點的左兒子和右兒子。
二項隊列
二項隊列不是一顆堆序的樹,而是堆序的樹的集合,稱為森林。堆序樹中的每一棵都是有約束的形式,叫做二項樹。
每一個高度上至多存在一棵二項樹。
二項隊列操作
最小元可以通過搜索所有的樹的根來找出。如果我們記住當最小元在其他操作期間變化時更新它,那么我們也可以保留最小元的信息並以O(1)時間執行該操作。
將高度相同的子樹合並。
二項隊列的實現
DeleteMin操作需要快速找出根的所有子樹的能力,因此,需要一般樹的標准表示方法:每個節點的兒子都存在一個鏈表中,而且每個節點都有一個指向它的第一個兒子的指針。諸兒子按照他們子樹的大小排序。當兩棵樹被合並時,其中一棵樹作為兒子被加到另一棵樹上。
二項樹的每一個節點將包含數據,第一個兒子以及右兄弟。二項樹中的諸兒子以遞減次序排列。
1.typedef struct BinNode *Position; 2.typedef struct Collection *BinQueue; 3. 4.struct BinNode 5.{ 6. int Element; 7. Position LeftChild; 8. Position NextSibling; 9.}; 10. 11.struct Collection 12.{ 13. int CurrentSize; 14. Position TheTree[MaxTrees]; 15.}; 16. 17. 18.Position CombineTrees(Position T1, Position T2) { 19. if (T1->Element>T2->Element) 20. { 21. return CombineTrees(T2, T1); 22. } 23. T2->NextSibling = T1->LeftChild; 24. T1->LeftChild = T2; 25. return T1; 26.}