Introuduction to Binary Heap 二叉堆介绍
Preface
This article belongs to the Data Structure series, which give some brief introduction to Tree,Heap,and Hash. Binary Heap is an important basic structure and has great efficiency in Sorting problems (Heap Sort). If there is any error in the article, please feel free to point it out and I will be really aprreciated with it.
We will start from the properties of Binary Heap, which tells you what can be regarded as a binary heap. Then, we introduce the basic operations of it, like insertion, deletion, and build. Enjoy!
一、What is binary Heap?
Binary heap is one of the most common used heaps. It can also be used as a priority Queue (A Queue with order so that any element with higher priority will be pop first, and the elements with the same order will be pop following FIFO principle)
It’s a binary tree with 2 key properties:
Structure Property:
It must be a complete tree, which means all levels except possibly the last are full( Full means each node is either a leaf or has 2 children). And all nodes in the last level have to be as left as possible.
This property makes it extremely easy to implement the binary heap with array.
Order Property:
There are 2 kinds of Binary Heap: Min Heap and Max Heap. Let’s take Max Heap as an example. In Max Heap, the element in root must be the maximum element in the whole heap, and element in the parent nodes must be bigger or equal to that of the children nodes. Note: There is no sorting information in the heap, no comparison between nodes in the same level.
二、Operations of Binary Heap (Max Heap)
1.Basic Struct
How can we represent a binary heap?
Since heap is a complete binary tree, we usually represent it as an array:
Root Node: Array[0]
For the i-th Node:
Its parent node: Array[(i-1)/2]
Left child: Array [2 * i+1]
Right child: Array [2 * i+2]
Last Element: Array[HeapSize-1]
There won’be any gap in the array since the tree is complete.
typedef int Element
struct HeapStruct{
int Size;//Current number of Elements
int Capacity;//Maximum element
Element* Array;
}
typedef struct HeapStruct* Heap;
//2 common boolean function
int isEmpty(Heap H){
return H->size==0;
}
int isFull(Heap H){
return H->size==H->capacity;
}
2.Insert
There are 2 Steps of Insertion.
Step1:
We insert the new input to the end, update the size information.
Step2:
Since the insertion breaks the Order Principle of heap, we need to keep comparing the new input and the element (Let’s say, A) in its parent node. We switch the postion of the new input and A until: new input is smaller or equal to A or new input is at the root.
Here’s the code in details:
//int has been type redefined as Element
//This helps a lot when you want to change the input type
void insert(Heap H,Element input){
if(isFull(H)){
printf("Heap is Full!\n");
return;
}
//Step1:
H->size++;
int childIndex=H->size-1, parentIndex=(childIndex-1)/2;
//Step2:
while(parentIndex>=0&& input>H->Array[parentIndex]){
H->Array[childIndex]=H->Array[parentIndex];
childIndex=parentIndex;
parentIndex=(childIndex-1)/2;
}
H->Array[childIndex]=input;
return;
Speed: O(logn)
3.Pop_Max
To Pop the max value, there are also 2 steps.
Step1:
We first create a variable to store the max value, which is at the root node. Then, we delete the root value, and use the last element to fill the blank. Remember to update the heap size.
Step2:
After Step1, the order property of the heap might be broken. Then, we first find the bigger value of the new root’s children, and switch the new root’s position with the bigger child. Keep this process until the new root value is bigger than both of its children or it reaches the leaf level.
Remember to return the variable you used to store the old root value. Below is the code in detail:
Element Pop_Max(Heap H){
//Step1:
Element Temp=H->Array[0];//Use the Temp to store the return value
Element lastElement=H->Array[H->size-1];//Get the value of the last
// element.
H->size--;//Update the size
int parentIndex=0,childIndex=parentIndex*2+1;//Suppose the last element has been put at the root
//Step2:
while(childIndex<=H->size-1){//Not reaches the leaf level
if(childIndex<H->size-1){//If the node has 2 children
if(H->Array[childIndex]]<H->Array[childIndex+1])
childIndex++;//Find the bigger child
}
if(lastElement>=H->Array[childIndex])
break;//Bigger than both, end.
H->Array[parentIndex]=H->Array[childIndex];
parentIndex=childIndex;
childIndex=parentIndex*2+1;
}
H->Array[parentIndex]=lastElemen;
return Temp;
Speed: O(logn)
4.get_Max
Root is the maximum value due to the order principle
Element get_Max(Heap H){
if(isEmpty(H)){
printf("Heap is Empty!\n");
exit(1);
}
return H->Array[0];
}
Speed: O(1)
5.Build_Heap
Simple Approach
It’s very easy to build heap, you just need to allocate some space for the heap structure and insert all the elements into the heap:
Heap Build_Heap(int MaxSize){
Heap H=malloc(sizeof(struct HeapStruct));
H->size=0;
H->capacity=MaxSize;
H->Array=malloc(sizeof(Element)*(MaxSize+1));
}
//And just use the function insert to do the insertion of all elements
Better Approach:
The time complexity of the previous approach is O(n*logn). Can we do better? The answer is yes, we just need to insert all the elements in the heap without considering the Order Property, which means, do not care which one is bigger and just follow the input flow. Then, we start adjusting the order property of the last non-leaf node in reverse level order.
Codes are below:
Heap buildHeap(int size, Element* list) {
Heap H = malloc(sizeof(struct HeapStruct));
H->size = size;
H->capacity = size + 1;
H->Array = malloc(sizeof(Element) * (size + 1));
for (int i = 0;i < size;i++)
H->Array[i] = list[i];
int parent = (size-1 - 1) / 2;
int children, Temp;
while (parent >= 0) {//Reverse Order
Temp = H->Array[parent];
children = parent * 2 + 1;
int localchildren = children, localparent = parent;
if (children == size - 1) {
if (Temp < H->Array[children])
{
H->Array[parent] = H->Array[children];
H->Array[children] = Temp;
}
}
else {
do {
if (H->Array[localchildren] < H->Array[localchildren + 1])
localchildren++;
if (Temp < H->Array[localchildren]) {
H->Array[localparent] = H->Array[localchildren];
H->Array[localchildren] = Temp;
}
localparent = localchildren;
localchildren = localparent * 2 + 1;
} while (localchildren<=size-1&&Temp < H->Array[localchildren]);
}
parent--;
}
6. Delete Heap
void Delete_Heap(Heap H){
free(H->Array);
free(H);
}
Conlusion:
This article is aiming at helping you to better understand the inside thoughts of binary heap. So some codes here might not be that clean, to help you better understand. If there is more time, I will introduce the 2 applications of heap: Heap Sort and K-th Smallest Element later. Anyway, if there is any question, feel free to comment.
(By the way, if you have better idea of the second Algorithm of Build_Heap, please let me know. I think it could be more clean with a little adjustment)