Implementation of binary heap

Time：2021-11-24

definition

A binary heap is a binary tree, i.eEach node has only one parent nodealsoEach node can have up to two child nodesTree structure. Binary heap is divided into maximum heap and minimum heap. If the value of any node in the heap isGreater than or equal toIts child nodes are called maximum heap. If the value of any node in the heap isLess than or equal toIts child nodes are called the minimum heap.

The binary tree in the following figure is a maximum heap, and each node in the tree has aGreater than or equal toChild nodes of this node: Complete binary tree

Binary heap is a complete binary tree, that is, when a subtree has a right node, the subtree must have a left node. We will see that the nodes of this binary tree are arranged continuously from left to right. A more rigorous definition of a complete binary tree is:

If the depth of the binary tree is h, the number of nodes in all layers (1 ~ h-1) except layer h reaches the maximum, and all nodes in layer h are continuously concentrated on the left.

The following figure is a complete binary tree with nodesTop to bottom, left to rightOne node is stored in turn: basic operation

• Insert node: inserts a node into the binary heap.
• Take out the maximum (minimum) node: take out the root node of the binary heap.

application

• Priority queue
• Heap sort
• solvetop Kproblem

Implementation of binary heap

The following specific implementations areTake the maximum heap as an exampleThat is, the root node of the binary heap is greater than or equal to all its child nodes. The operation of the minimum heap is the same as that of the maximum heap, but the comparison relationship between nodes is different.

storage

Because the binary heap is a complete binary tree, we can use an array to store it. Each element of the array is each node in the binary heap. This is because the nodes of a complete binary tree are continuous from left to right. If it is not a complete binary tree, the indexes of nodes in the array will be discrete and discontinuous, which will waste memory space. When you want to storeNWhen a binary stack of nodes, the length isN+1To store this binary heap. Array index is1The element is the root node of the binary heap. Indexes0The location of is empty. This is to facilitate the calculation of the indexes of the child nodes and parent nodes of each subtree.

If the index of a node of the tree in the array is k, then:

• Parent node index of this node: K / 2 (rounded down)
• Left child node index of this node: 2 * k
• Index of the right child node of this node: 2 * k + 1

The number on each node in the following tree represents the index of the node in the array, and the number in the node represents the element value corresponding to the node. The tree is in the arraystorage structure Is: Where the index is3The indexes of its parent and child nodes are:

• Parent node index:3 / 2 = 1(rounded down)
• Left child node index:2 * 3 = 6
• Right child node index:2 * 3 + 1 = 7

Binary heap initialization code implementation:

class BinaryHeap {
private:
//Array for storing binary heap
int *container = NULL;
//Capacity, that is, the maximum number of nodes can be stored
unsigned int capacity = 0;
//Size of binary heap
unsigned int size = 0;
public:
BinaryHeap(unsigned capacity) {
assert(capacity > 0);
//Initializes the array that holds the binary heap
this->container = new int[capacity];
this->capacity = capacity;
}
};

Index calculation code implementation

//Left child node index
unsigned calculateLeftChildIndex(unsigned index) {
return index * 2;
}
//Right child node index
unsigned calculateRightChildIndex(unsigned index) {
return this->calculateLeftChildIndex(index) + 1;
}
//Parent node index
unsigned calculateParentIndex(unsigned index) {
return index / 2; //  If both sides of C + + division are integers, it defaults to rounding down
}

Insert node

Inserting a node is to add a node to the heap, and the corresponding binary heap cannot be destroyed.

When inserting a node, first add the node to be inserted to the binary heapPile tail(equivalent to adding a new element to the end of the array). At this time, the newly inserted node in the binary tree may be larger than its parent node, and the current binary tree no longer meets the definition of maximum heap. This node needs to be swapped with its parent node. After the exchange, the new parent node may still be larger than the parent node of the new parent node. Then repeat the previous operation again, moving up layer by layer until the heap top or the encountered node is no longer larger than its parent node. The operation of the entire switching node is calledshiftUp, when the operation is completed, the binary tree meets the definition of maximum heap again.

The binary stack on the left of the figure belowPile tailInsert node10, get the tree on the right. Discover newly added nodes10Than its parent node6Even larger, the tree on the right violates the definition of binary heap and is no longer a binary heap. Therefore, we need to determine the location of the newly added node (that is, the node)10Where) doshifUpOperation: In the tree on the left of the figure belowNode 10Better than itParent node 6To be large, they need to exchange positions to get the right tree: In the tree on the left of the figure belowNode 10Still better than itParent node 9To be large, so they swap positions again to get the tree on the right: Node 10Finally, it is moved to the root node, and any node in the observation tree is larger than its child nodes. In this way, the whole binary tree meets the definition of binary heap again. The whole process of repairing the binary tree isshiftUpOperation. Insert node implementation code

void insert(E element) {
assert(!this->isFull());
this->container[this->size] = element;
this->shiftUp(this->size);
this->size ++;
}

Shiftup implementation code

void shiftUp(unsigned currentIndex) {
unsigned parentIndexOfCurrentIndex = this->calculateParentIndex(currentIndex);
while ((index > 1) && this->compare(this->container[index], this->container[parentIndexOfCurrentIndex])) {
swap(this->container[index], this->container[parentIndexOfCurrentIndex]);
currentIndex = parentIndexOfCurrentIndex;
parentIndexOfCurrentIndex = this->calculateParentIndex(currentIndex);
}
}

whileMediumindex > 1Indicates that the index is not a heap top index,this->container[index] >= this->container[parentIndexOfCurrentIndex / 2]Indicates that the node is larger than its parent node. As long as these two conditions are met at the same time, the location will be exchanged, and the index will be set as the parent node of the original node, moving up layer by layer until either of these two conditions is not met.

Delete node

When deleting a pin from a binary heap, only the node at the top of the heap can be deleted at a time. For the largest heap, it means deleting the largest node in the heap each time. If the heap is used to implement the priority queue, deleting a node from the heap can be understood as taking the node with the highest priority from the heap.

holdPile topAfter the node is deleted, thePile tailNode moved toPile topThe location of the. Generated in a new binary treePile topThe node of may be smaller than its child nodes. At this time, the binary tree no longer meets the definition of binary heap. Need to put newPile topThe node of exchanges positions with the largest of its child nodes. After exchangeNew child nodeIt’s still possibleNew child nodeIf the child node is small, repeat the previous operation again. This moves down layer by layer until the encountered node is no longer smaller than its child nodes or to the bottom of the heap. The operation of the entire switching node is calledshiftDown, when the operation is completed, the binary tree meets the definition of maximum heap again.

When deleting a pin from the tree on the left, find it firstPile topPositionalNode 10, and then delete it to get the right tree: Next, put the left in the treePile tailPositionalNode 6Move toPile topTo get the right tree: In the tree on the leftPile topPositionalNode 6Better than itTwo child nodesEven smaller, let the node exchange positions with the largest of its child nodes. Its two child nodes are8and9, the largest child node is9, soNode 6AndNode 9Exchange positions and get the right tree after exchange: After completing the above operation,Node 6There is no larger node in the child node of, so terminate the operation and finally get the tree in the figure below. It re satisfies the definition of binary heap. Specific code

void shiftDown(unsigned currentIndex = 1) {
unsigned leftChildIndexOfCurrent = this->calculateLeftChildIndex(currentIndex);
//Judge whether the index of the left child node of the current node exceeds the size of the array. If it exceeds, it means that the node has no left child node
While (this - > getsize() > = leftchildindexofcurrent) {// if the node has left child nodes
unsigned maxChildIndexOfCurrent;
unsigned rightChildIndexOfCurrent = calculateRightChildIndex(currentIndex);
If (this - > getsize() > = rightchildindexofcurrent) {// if the node has a right child node
If (this - > container [rightchildindexofcurrent] > this - > container [leftchildindexofcurrent]) {// if the right child node is larger than the left child node
maxChildIndexOfCurrent = rightChildIndexOfCurrent; //  If the right child node is larger than the left child node, the largest child node is set as the right child node
} else {
maxChildIndexOfCurrent = leftChildIndexOfCurrent; //  Otherwise, the largest child node is set as the left child node
}
}
If ((this - > container [currentindex] > = this - > container [maxchildindex])) {// if the current node is greater than or equal to its largest child node, the definition of the largest heap is not broken, so just break it
break;
}
//If the current node has a child node larger than it, the positions of the two nodes are exchanged
swap(this->container[currentIndex], this->container[maxChildIndexOfCurrent]);
//Set the index of the current cycle as the index of the new root node, and then continue to check down
currentIndex = maxChildIndexOfCurrent;
leftChildIndexOfCurrent = this->calculateLeftChildIndex(currentIndex);
}
}

The code implementation idea is to judge firstCurrent nodeDoes it existLeft child node。 Then judgeCurrent nodeDoes it existRight child node。 If it exists, find out the two child nodesLargest child node。 Then judgeCurrent nodewhetherGreater than or equal to the largest child node in its child nodesIf the point is true, exit because it does not violate the definition of maximum heap. If it doesn’t work, it meansThe current node has a child node larger than itThen, the current node and its largest child node are exchanged, and the index of the current cycle is set as the index of the new root node, and then continue to check down.

Construction of binary reactor

holdNElements are constructed into a binary heap.

holdNThere are generally two ways to construct an element into a binary heap:

• The first method is through binary heapInsert nodeThe operation inserts each element into the heap in turn.
• The second common way is to put thisNThe elements are stored in an array. At this time, you can think of the array as aComplete binary tree, but it’s not a binary heap yet. Then start back to the last element of the array in turnshiftDownThe purpose of this step is to ensure that each subtree in the binary heap meets the definition of the binary heap. But we found that if a node is aLeaf nodeIf so, it does not need to do any operation at all, because it has no child nodes, even if it doesshiftDownOperation is not done. So after optimization, we can start fromThe last node that has childrenStart going backshiftDown。 The last node with children is also calledNon leaf node, its index isThe parent node of the heap tail nodeIndex of. This method is calledheapify

Heapify implementation

heapify(int *arr, unsigned size) {
//Size is the position at the end of the heap, and its parent node is the last node with children
for (int i = this->calculateParentIndex(size); i >= 1; i --) {
this->shiftDown(i);
}
}

Compatible with minimum heap and maximum heap

Sometimes we need as like as two peas, but sometimes we need to use the largest heap, but the two codes are almost the same, but the direction of the comparison symbol is different. In order to reuse the code, we only need to set a comparison method for the two fork heap, such as when it initializes the largest heap, it will bring in a method. This method is used for comparison. It is good to call this method when comparison is needed in binary heap.

If it is a minimum heap, our comparison method is as follows:

bool compare(int a, int b) {
return a < b;
}

For example, when calling at the corresponding place in the heap:

if (this->compare(this->container[calculateRightChildIndex(index)], this->container[calculateLeftChildIndex(index)])) {
// ........
}

Conclusion

This paper expounds the definition and common operations of heap. The code in this paper uses C + +. The focus of data structure and algorithm is thought and logic, so you can use any language you are familiar with after understanding the idea of algorithm. An article on binary heap application and complexity analysis will be published later.

Complete code:https://github.com/acodercat/cpp-algorithm…

The binary heap in the warehouse code starts from index 0, so the calculation of the parent node index will be different.