Summary and Thinking of Heap Sort in "Elementary Data Structure"

1. The focus of this chapter 

  • heap
  • adjust up
  • adjust down
  • heap sort

Second, the heap

2.1 Introduction to the heap (three points)

1. The physical structure is an array

2. The logical structure is a complete binary tree

3. Large heap: all parent nodes are greater than or equal to child nodes, small heap: all parent nodes are less than or equal to child nodes.

2.2 Upward adjustment

Concept: There is a small/large heap, insert an element at the end of the array, and adjust the heap to make the heap still small/large.

Conditions of use: The first n-1 elements of the array form a heap.

Take a large heap as an example:

Logic implementation:

Treat the newly inserted last element as a child, and compare it with the parent. If the child is greater than the parent, swap them, treat the parent as the child, and compare them in turn until the child is equal to 0 to end the adjustment.

If the child is smaller than the father in the middle, it will jump out of the loop and end the adjustment.

Reference Code:

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])//如果孩子大于父亲,则将它们交换。
		{
			Swap(&a[child], &a[parent]);
            //迭代过程:
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
            //如果孩子小于父亲,结束调整
			break;
		}
	}
}

Adjust app up

After adding new elements to the big\small heap, the big\small heap is still the big\small heap.

2.3 Downward adjustment

Concept: The left and right subtrees of the root node are all large/small heaps. By adjusting downwards, the entire complete binary tree is made up of large/small heaps.

Conditions of use: The left and right subtrees of the root node are all large\small heaps.

 As shown in the figure, the root is 23, and its left and right subtrees are large heaps, but the entire complete binary tree is not a heap. By adjusting downwards, the entire complete binary tree can be a heap.

Logic implementation:

Select the larger child of the left and right children of the root, then compare it with the root, if it is larger than the root, exchange it, otherwise end the adjustment.

Reference Code:

void AdjustDown(HPDataType* a, int size, int root)
{
	int parent = root;
	int child = parent * 2 + 1;//左孩子
	while (child < size)
	{
		if (child + 1 < size && a[child] < a[child + 1])//如果左孩子小于右孩子,则选右孩子
		{
			//务必加上child+1,因为当child=size-1时,右孩子下标是size,对其接引用会越界访问。
            child++;//右孩子的下标等于左孩子+1
		}
		if (a[child] > a[parent])//让较大的孩子与父亲比较,如果孩子大于父亲,则将它们交换。
		{
			Swap(&a[child], &a[parent]);
            //迭代过程
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

2.4 Build heap (two ways)

The first one: upward adjustment to build the heap (time complexity is O(N*logN), space complexity O(1))

The idea is to adjust upwards sequentially from the second array element to the last array element.

Reference Code:

for (int i = 1; i < n; i++)
{
	AdjustUp(a, i);
}

 Time complexity calculation:

Compute with a full binary tree

The worst case number of execution steps is: T=(2^1)*1+(2^2)*2+(2^3)*3+....+2^(h-1)*(h- 1)

The final simplification is: T=2^h*(h-2)+2

And because (2^h)-1=N

So h=log(N+1)

After bringing in T=(N+1)*(logN-1)+2

So its time complexity is: O(N*logN)

The second: downward adjustment to build the heap (time complexity is O(N), space complexity is O(1))

Adjust downwards from the last non-leaf node (the parent of the last array element) to the first array element.

Reference Code:

//n代表数组元素个数,j的初始值代表最后一个元素的父亲下标
for (int j = (n - 1 - 1) / 2; j >= 0; j--)
{
	AdjustDown(a, n, j);
}

Time complexity calculation:

Compute with a full binary tree

Worst execution count:

T=2^(h-2)*1+2^(h-3)*2+2^(h-4)*3+.....+2^3*(h-4)+2^2*(h-3)+2^1*(h-2)+2^0*(h-1)

Simultaneous 2^h-1=N

Simplify to get T=N-log(N+1)

When N is large, log(N+1) can be ignored.

So its time complexity is O(N).

Therefore, we generally use the downward adjustment method to build heaps.

3. Heap sort

The current best sorting algorithm has a time complexity of O(N*logN)

The time complexity of heap sort is O(N*logN)

Heap sort is to sort the heap, so when we sort an array, we first build the array into a heap, and then sort it.

The first thing to know is:

Ascending an array requires building the array into a large heap.

Descending an array requires building the array into small heaps.

Why is this?

This requires understanding the difference between a large heap and a small heap. The top of the large heap is the largest number, and the top of the small heap is the smallest number.

When we build the heap for the first time, building a large heap can get the first maximum number, and then we can exchange it with the last element of the array. Next time we only need to adjust the number at the top of the heap down again, and we can change the array again. It becomes a large heap, and then it is exchanged with the penultimate element of the array. Since then, the two elements have been arranged so that they are stored where they are needed, and then the numbers are fetched and adjusted in turn.

And if it is a small heap, when the heap is first built, we can get the smallest number, and then put it in the first position of the array, and then you want to keep it or a small heap, what should we do? You can only build a heap from the bottom starting from the second element, and the time complexity of building a heap is O(N). You need to constantly rebuild the heap. The final time complexity of heap sorting is O(N*N). Sensible.

Or after building the small heap, you do this:

When opening a space of n arrays, select the first smallest number and save it in the newly opened array space, then delete the number of the top of the heap, and then adjust the number of the top of the heap down, and then put the new top of the heap. The number is placed in the second position of the new array.......

Although such time complexity is O(N*logN).

But such space complexity is O(N).

Nor is it the optimal heap sort method.

The advantage of building a large heap is that it puts the selected number at the end, so that we can adjust the top of the heap downward, so that it is still a large heap, and the time complexity of downward adjustment is O(logN), The time complexity of the final heap sort is O(N*logN).

The core meaning of heap sort:

By building a large heap or a small heap, select the largest or smallest number in the heap, and place them from back to front.

Reference Code:

    int end = n - 1;//n代表数组元素的个数
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}

Code for the whole heap sort:

void Swap(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustDown(int* a, int n, int root)
{
	int child = 2 * root + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child+1])
		{
			child++;
		}
		if (a[child] > a[root])
		{
			Swap(&a[child], &a[root]);
			root = child;
			child = 2 * root + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	//建大堆(向上调整)
	//for (int i = 1; i < n; i++)
	//{
	//	AdjustUp(a, i);
	//}

	//建大堆(向下调整)
	for (int j = (n - 1 - 1) / 2; j >= 0; j--)
	{
		AdjustDown(a, n, j);
	}
	//升序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
void printarr(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 9,2,4,8,6,3,5,1,10 };
	int size = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr, size);
	printarr(arr, size);
	return 0;
}

Guess you like

Origin blog.csdn.net/m0_62171658/article/details/124004650