Data structure: Common sorting algorithm (5): Heap sorting (C++ implementation)

Data structure: common sorting algorithm (5): heap sort

Heap sort is a kind of tree selection sort, which is an effective improvement to direct selection sort. Heap is a special tree data structure, that is, a complete binary tree. The heap is divided into a large root heap and a small root heap. The large root heap means that the value of the root node is greater than the value of the two child nodes; the small root heap means that the value of the root node is less than the value of the two child nodes, and the two subtrees of the root node are also one heap.

The definition of a heap: a sequence with n elements (h1, h2,..., hn), if and only if (hi>=h2i,hi>=2i+1) or (hi<=h2i,hi<=2i +1) (i=1,2,...,n/2) is called a heap. Only the heap that meets the former conditions is discussed here. It can be seen from the definition of the heap that the top element (that is, the first element) must be the largest item (large top heap). A complete binary tree can intuitively represent the structure of the heap. The top of the heap is the root, and the others are the left and right subtrees. (It can be extended to pre-order traversal, middle-order traversal, and post-order traversal)

1. Basic idea :

Initially, the sequence of numbers to be sorted is regarded as a binary tree stored sequentially, and their storage order is adjusted to make it a heap. At this time, the number of root nodes of the heap is the largest. Then swap the root node with the last node of the heap. Then readjust the previous (n-1) number to make it a pile. And so on, until there are only two nodes in the heap, and exchange them, and finally get an ordered sequence of n nodes. From the algorithm description point of view, heap sorting requires two processes, one is to build a heap, and the other is to exchange positions between the top of the heap and the last element of the heap. So heap sort has two functions. One is the penetration function for building a reactor, and the other is a function that calls the penetration function repeatedly to achieve sorting.

​ The difficulty is (1) how to generate a sequence into a big heap

​ (2) After outputting the top element of the heap, how to make the remaining elements generate a big root heap

Ideas:

  • Step 1: Build a big root pile-build a big root pile from an unordered sequence of n elements,

  • Step 2: Exchange heap elements-exchange the end element and the head element, so that the end element is the largest element;

  • Step 3: Rebuild the big root pile-adjust the disordered sequence composed of the first n-1 elements to the big root pile

    Repeat steps two and three until the entire sequence is in order.

2. Examples

Example 1: arr[]

img

Image source: https://www.cnblogs.com/zwtgyh/p/10631760.html

#include<iostream>
#include<vector>
using namespace std;

// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
	int left = 2 * index + 1; // index的左子节点
	int right = 2 * index + 2;// index的右子节点

	int maxIdx = index;
	if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
	if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;

	if (maxIdx != index)
	{
		swap(arr[maxIdx], arr[index]);
		adjust(arr, len, maxIdx);
	}

}

// 堆排序
void heapSort(vector<int> &arr, int size)
{
	// 构建大根堆(从最后一个非叶子节点向上)
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		adjust(arr, size, i);
	}

	// 调整大根堆
	for (int i = size - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
		adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
	}
}

int main()
{
	vector<int> arr = { 8,6,7,4,5,3,2,1 };
	heapSort(arr, arr.size());
	for (int i = 0; i<arr.size(); i++)
	{
		cout << arr[i] <<"  ";
	}
	cout << endl;
	return 0;
}

Example 2: arr[4,6,8,5,9] sort by heap sort

  • Step 1: Build a big root heap

① Unordered sequence to build a complete binary tree

image

② From the last leaf node, adjust from left to right, from bottom to top, and adjust the complete binary tree to a large root pile

a. Find the first non-leaf node 6, because the right child node of 6 is greater than 6, so exchange 6 and 9. After the exchange, it conforms to the structure of the big root pile.

image

c. Find the second non-leaf node 4, since the left child node of 4 is greater than 4, so swap 4 and 9. After the exchange does not conform to the structure of the big root pile, continue to adjust from right to left and from bottom to top.

image

image

  • Step 2: Exchange heap elements (swap head and tail elements-get the largest element)

image

  • Step 3: Rebuild the big root pile (the first n-1 elements)

image

  • Repeat steps 2 and 3 until the entire sequence is in order

image

Image source: https://www.cnblogs.com/chengxiao/p/6129630.html

Example code:

#include<iostream>
#include<vector>
using namespace std;

// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
	int left = 2 * index + 1; // index的左子节点
	int right = 2 * index + 2;// index的右子节点

	int maxIdx = index;
	if (left<len && arr[left] > arr[maxIdx])     maxIdx = left;
	if (right<len && arr[right] > arr[maxIdx])     maxIdx = right;

	if (maxIdx != index)
	{
		swap(arr[maxIdx], arr[index]);
		adjust(arr, len, maxIdx);
	}

}

// 堆排序
void heapSort(vector<int> &arr, int size)
{
	// 构建大根堆(从最后一个非叶子节点向上)
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		adjust(arr, size, i);
	}

	// 调整大根堆
	for (int i = size - 1; i >= 1; i--)
	{
		swap(arr[0], arr[i]);           // 将当前最大的放置到数组末尾
		adjust(arr, i, 0);              // 将未完成排序的部分继续进行堆排序
	}
}

int main()
{
	vector<int> arr = { 4, 6, 8, 5, 9 };
	heapSort(arr, arr.size());
	for (int i = 0; i<arr.size(); i++)
	{
		cout << arr[i] <<"  ";
	}
	cout << endl;
	return 0;
}

3. Summary

  • The time complexity of heap sorting is mainly composed of two parts: the process of initializing the heap and rebuilding the heap after popping the top element each time
  • The time complexity of the initialization process of building a heap is O(n): Assuming that the height of the heap is k, starting from the node on the right of the second-to-last layer, the nodes of this layer must compare child nodes and choose whether to exchange, the third-to-last The layers are similar, all the way to the first layer (that is, the number of layers is from k-1 to 1); then the total time is (2 (i-1))*(ki), where i represents the i-th layer (range is k-1) To 1), 2 (i-1) indicates how many elements are on the layer, (ki) indicates the number of comparisons on the subtree, that is, S = 2^(k-2)*1 + 2^(k-3) 2 + 2^(k-4) 3 +… + 2^1 (k-2) + 2^0 (k-1), using staggered subtraction (use constant 2 to assist conversion, multiply both sides by 2 and subtract Remove the original equation) to get S = 2^(K-1) + 2^(K-2) + 2^(K-3) +… + 2-(K-1), ignoring the last constant term is just waiting The ratio sequence, that is, S=2 k-2-(k-1)=2 kk-1, and because k is the depth of a complete binary tree, so 2^k <= n <2^k-1, we can consider k = logn, in summary, S = n-logn -1, so the time complexity is O(n)
  • The time complexity of the process of rebuilding the heap after popping the top element of the heap is O(nlogn): loop n-1 times, each time from the follow node to the loop search, so each time is logn, the total time is (n-1)* logn = nlogn-logn
  • Therefore, the time complexity of heap sort is O(n) + O(nlogn) = O(nlogn)
  • Heap sort is grounded sort, so the space complexity is constant O(1)

Guess you like

Origin blog.csdn.net/qq_43801020/article/details/108136124