[Data Structure and Algorithm] Heap Sort

heap sort

Priority queue (priority queue) is a queue arranged according to a certain priority. Elements with higher priority will be dequeued earlier, and elements with the same priority will be processed according to the principle of first-in, first-out. The basic algorithm of priority queue can be modified on the basis of ordinary queue. For example, when entering the queue, insert the element at the end of the queue, and find out the element with the highest priority when dequeue; or insert the element into the appropriate position according to the priority when entering the queue, and dequeue the element at the head of the queue when dequeueing. These two implementation methods, enqueue or dequeue always have a time complexity of O ( n ) {O(n)}O ( n ) . And using the heap to implement the priority queue, the time complexity of entering and exiting the queue isO ( log 2 n ) {O(log_{2}n)}O(log2n)

heap definition

A heap can be defined as a binary tree whose nodes contain keys (one key per node) and satisfy the following two conditions:

(1) The shape property of the tree requires that the binary tree is essentially complete (or simply called a complete binary tree), which means that each layer of the tree is full, except the last layer. Elements on the right may be missing.

(2) Parental dominance requirements, also known as heap properties, the key of each node must be greater than or equal to the key of its children (big root heap), or the key of each node must be less than or Equal to the keys of its children (small root heap) (for any leaf we consider this condition to be automatically satisfied).

If the heap is numbered from 0 to n according to the sequence, the following relationship is satisfied between the nodes:

insert image description here

It can be seen from the definition of a heap that if a complete binary tree is a heap, the root node (called the top of the heap) must be the largest (big root heap) or smallest (small root heap) of all nodes in the current heap, as shown in the figure below Show. A priority queue implemented with a small root heap is called a minimal queue, and a priority queue implemented with a large root heap is called a maximal queue.

insert image description here

heap construction

The logical structure of the heap is a complete binary tree, and the storage structure of the heap is generally stored in an array. Taking the big root heap as an example, the heap can be defined as an array H [ 0.. n ] H[0..n]H [ 0.. n ] , where, in the first half of the array, the element at each position i is always greater than or equal to the element in position 2i+1 and 2i+2, that is, the following relationship exists: H [ i ] ≥
max { H [ 2 i + 1 ] , H [ 2 i + 2 ] } i = 0... ⌊ ( n + 1 ) / 2 − 1 ⌋ H[i] \ge max\{H[2i+1], H[2i+2]\} \\ i=0...\lfloor{(n+1)/2-1}\rfloorH[i]max{ H[2i+1],H [ 2i _+2]}i=0...(n+1)/21
According to this characteristic, there are two main methods of constructing a heap:

  1. Bottom-up heap construction
  2. top-down heap construction

Bottom-up heap construction

When initializing a complete binary tree containing n nodes, we place the keys in the order given, and then "heap" the tree as follows. Starting from the last parent node and going up to the root, the algorithm checks whether the keys of these nodes satisfy the parent dominance requirement . If the node is not satisfied, the algorithm exchanges the key K of the node with the largest key (minimum key) of its children, and then checks whether K satisfies the parental dominance requirement in the new position. This process continues until the parental dominance requirement on K is satisfied (eventually it must be satisfied, since this condition is automatically satisfied for keys in each leaf). For the subtree rooted at the current parent node, after completing its "heaping", the algorithm does the same for the node's immediate predecessors. After this operation is done on the root of the tree, the algorithm stops.

insert image description here

// 堆构造
template<typename T>
void HeapBottomUp(vector<T>& H){
	int n = H.size();
	for(int i=n/2-1; i>=0; i--){
		int k=i;
		T v = H[k];
		bool heap = false;
		while(!heap && 2*k+1<n){
			int j=2*k+1;
			if(j+1<n && H[j] < H[j+1]){// 存在两个子女 
				j = j+1;
			}
			if(v>=H[j]){
				heap=true;
			}else{
				H[k]=H[j];
				k=j;
			}
		}
		H[k]=v;
	}
}

top-down heap construction

The top-down algorithm (less efficient) constructs a new heap by successively inserting new keys into a pre-constructed heap. First, a new node containing key K is appended after the last leaf of the current heap. Then filter K to its proper position according to the following method. Compare K with its parent's key: if the latter is greater than or equal to K, the algorithm stops (the structure is already a heap); otherwise, swap the two keys and compare K with its new parent. This exchange continues until K is no greater than its last parent, or until the root of the tree is reached.

insert image description here

Deletion from the top of the heap

Remove the root's key from a heap. The key to delete is swapped with the last key, and then we "heapify" (heap adjust) the smaller tree by swapping the new key in the root with the larger key in its children until the parent Advantage requirements, the specific process is shown in the figure below.

insert image description here

// 堆调整
template<typename T>
void adjustHeap(vector<T>& H, int k, int len){
	T temp = H[k];
	for(int i=2*k+1; i<len; i=2*i+1){
		if(i+1<len && H[i] < H[i+1]){
			i++;
		}
		if(H[i] > temp){
			H[k] = H[i];
			k = i;  
		}else{
			break;
		}
	}
	H[k] = temp; 
}

heap sort

Heapsort (heapsort) is an important sorting algorithm invented by JWJ Williams. The implementation process of this two-stage algorithm is divided into two parts:

Step 1 : Construct a heap, that is, construct a heap for a given array.

Step 2 : Delete the largest key, then adjust the heap, and execute the second step in a loop until all elements are deleted.

The end result is that the elements of the array are removed in descending order. But for the array implementation of the heap, an element being removed comes last, so the resulting array will be exactly the original array in ascending order.

insert image description here

The process of constructing the heap in the first step in the figure uses a bottom-up construction method to construct the array into a large root heap!

insert image description here

In the second step in the figure, each time the top of the heap is exchanged with the last element (deleting the largest key), and the remaining part (elements that have not been deleted before) is adjusted for the heap. After multiple rounds of this cycle, we get Arrays in ascending order.

In fact, whether it is the worst case or the average case, the time efficiency of heap sorting belongs to O ( nlogn ) O(nlogn)O ( n log n ) . _ _ Therefore, the time efficiency of heap sort and the time efficiency of merge sort belong to the same class. Also, unlike the latter, heapsort is in-place, that is, it doesn't require any additional storage space. Timing experiments on random files indicate that heapsort runs slower than quicksort, but is still competitive with mergesort.

Code

template<typename T>
void HeapSort(vector<T>& H){
	// 第一步:建堆 
	HeapBottomUp(H);
	// 第二步:删除最大键,并进行堆调整 
	for(int i=H.size()-1; i>0; i--){
		swap(H[0], H[i]); // C++中提供了swap函数 
		adjustHeap(H, 0, i);
	} 
}

Guess you like

Origin blog.csdn.net/zzy_NIC/article/details/121183809