Introduction to binary heap
The heap is generally a binary heap without any modification. Like a binary tree, heap has two properties: structural and heap sequence. Like the AVL Tree, this operation on the heap may destroy one of the two properties. Therefore, the heap operation must be terminated only when all the properties of the heap are satisfied.
Structure
A heap is a fully-filled binary tree. Because a fully-filled binary tree is regular, it can be represented by an array without a pointer. As shown in, the array in Figure 2 corresponds to the heap in Figure 1.
Figure 1: Binary heap Figure 2: Array Storage of binary heap
For any element on I, his left son is at location 2I, his right son is at location 2I + 1, and his father is at I/2. Therefore, not only are pointers not required here, but the operations required to traverse the tree are also very simple. The only problem with this representation is that the maximum heap size needs to be estimated in advance, but for typical scenarios, the heap size in Figure 2 is 13 elements. This array has a position of 0, which is used as a Sentel, which will be described later.
Therefore, the data structure of a heap is composed of an array, an integer representing the maximum value, and the size of the current heap.
Heap Sequence
The nature of fast operation execution is heap order. In a heap, for each node X, except for the root node (the root node has no parent node), the keywords in the father of X are smaller than (or equal to) X ). In Figure 3, the left side is the heap, and the right side is not (the dotted line indicates that the heap sequence is damaged ).
Figure 3: two full Binary Trees
Basic operations
Insert ):
To insert an element x into the heap, we create a hole in the next idle position. Otherwise, the heap will not be a full tree. If X can be put into this hole, the insertion is complete. Otherwise, we will move the elements on the parent node of the hole into the hole. In this way, the hole will go up to the root node. Continue this process until X can be put into the hole. Figure 4 shows that in order to insert 14, we create a hole in the next available position of the heap. Because the insert 14 hole breaks the heap sequence, 31 is moved into the hole, figure 5 continue this policy until the correct position of 14 is found.
Figure 4: Create a hole, and then click the hole. Figure 5: insert 14 to the remaining two steps in the previous heap.
This kind of policy is called consideration. Consider the new elements in the heap until they are located correctly. Use the following code to easily implement them.
If the new minimum value of the element to be inserted, it will always be pushed to the top, so that at a certain time point, I will be 1, and we need to make the program jump out of the while loop. Of course, this can be done through a clear test. However, here we use a very small value in the position 0 to terminate the while loop. This value must be smaller than any value in the heap, which is called a flag or a sentinel. This is similar to the use of header nodes in the linked list. By adding this tag, we can avoid executing a test every cycle. This is a simple space-to-time strategy.
Deletemin (delete the smallest element ):
It is easy to find the smallest element; the difficult part is to delete it. When a minimal element is deleted, a hole is generated at the root node. Because one element is missing in the heap, the last element x in the middle must be moved somewhere in the heap. If X can be put into a hole, deletemin is complete. However, this is generally not possible, so we move the two sons of the holes into the holes, so that the holes are pushed down one layer and repeat this step, we know that X can be put into holes. Therefore, we place X in a correct position along the path containing the minimum son starting from the root.
Figure 6 shows the heap before deletemin. After deleting 13, we must correctly put 31 in the heap, and 31 in the hole, because this will damage the heap sequence. Therefore, we put the smaller son 14 into the hole, and the hole slides down to a layer, repeat the process, put 19 into the hole, and create a new hole on the lower layer, then 26 is placed into the hole, and a new hole is created at the bottom layer. Finally, we can place 31 in the hole. This policy is called a concern.
Figure 6 create a hole at the root. Figure 7: Drop the hole layer
Figure 8: Move the hole to the bottom layer and insert 31binheap. h
typedef int ElementType;#ifndef _BinHeap_H#define MinPQSize 10#define MinData -32767struct HeapStruct;typedef struct HeapStruct *PriorityQueue;PriorityQueue Initialize(int MaxElements);void Destroy(PriorityQueue H);void MakeEmpty(PriorityQueue H);void Insert(ElementType X,PriorityQueue H);ElementType DleteMin(PriorityQueue H);ElementType FindMin(PriorityQueue H);int IsEmpty(PriorityQueue H);int IsFull(PriorityQueue H);#endif
Binheap. c
#include"BinHeap.h"#include"fatal.h"struct HeapStruct{ int Capacity; int Size; ElementType *Elements;};PriorityQueue Initialize(int MaxElements){ PriorityQueue H; if(MaxElements<MinPQSize) Error("Priority queue size is too small"); H=malloc(sizeof(struct HeapStruct)); H->Elements=malloc((MaxElements+1)*sizeof(ElementType)); if(H->Elements==NULL) FatalError("Out of space!!!"); H->Capacity=MaxElements; H->Size=0; H->Elements[0]=MinData; return H;}void MakeEmpty(PriorityQueue H){ H->Size=0;}void Insert(ElementType X,PriorityQueue H){ int i; if(IsFull(H)) { Error("Priority queue is full"); } for(i=++H->Size;X<H->Elements[i/2];i=i/2) { H->Elements[i]=H->Elements[i/2]; } H->Elements[i]=X;}ElementType DeleteMin(PriorityQueue H){ int i,Child; ElementType MinElement,LastElement; if(IsEmpty(H)) { Error("Priority queue is empty"); return H->Elements[0]; } MinElement=H->Elements[1]; LastElement=H->Elements[H->Size--]; for(i=1;i<H->Size;i=Child) { Child=2*i; if(Child!=H->Size&&H->Elements[Child]>H->Elements[Child+1]) Child++; if(LastElement>H->Elements[Child]) H->Elements[i]=H->Elements[Child]; else break; } H->Elements[i]=LastElement; return MinElement;}ElementType FindMin(PriorityQueue H){ return H->Elements[1];}int IsEmpty(PriorityQueue H){ return H->Size==0;}int IsFull(PriorityQueue H){ return H->Capacity==H->Size;}void Destroy(PriorityQueue H){ free(H->Elements); free(H);}
Usebinheap. c
#include <stdio.h>#include <stdlib.h>#include"BinHeap.h"int main(){ int i; PriorityQueue H=Initialize(MinPQSize); MakeEmpty(H); for(i=0;i<MinPQSize;i++) { Insert(i,H); } printf("Hello world!\n"); return 0;}
D-heap
Because the implementation of the binary heap is simple, the binary heap is almost always used when priority queue is required. The D-heap is a simple promotion of the binary heap. It is like a binary heap, but all nodes have d sons (therefore, the binary heap is also called a 2-heap ). Indicates a 3-heap. Note that the D-heap is much lighter than the binary heap, which improves the running time of the insert operation. However, for large D, deletemin operations are much time-consuming, because although the tree is too small, the smallest of D's sons must be found. If a standard algorithm is used, the D-1 comparison is used, so the operation time is increased. If D is a constant, the running time of both operations is O (logn ). Although an array can still be used, it is found that the multiplication and division of son and father both have a factor D. Unless D is a power of 2, the running time will be greatly increased, because we can no longer implement division and multiplication through binary shift. D-heap is interesting in theory because there are many algorithms that insert more times than delete, and when the priority queue is too large to fully load into the memory, d-heap is also very useful. In this case, D-heap can play a role in roughly the same way as B-tree.
Apart from the inability to execute the find operation (that is, to execute it in logarithm), it is difficult to merge the two heaps into one. This additional operation is called merge. There are many methods to implement the heap so that the merge operation runs at O (logn), for example, the left heap described in the next article.