Thinking about heap-related issues and second-level conclusions Finding the time complexity of heap sorting


  Foreword: Learn about heaps today. At the beginning, the stereotype of the heap was a binary tree, and the bottom layer thought it was formed by a linked list. After learning the knowledge of the heap, it turned out that it was implemented by an array. Simply put, the logical structure of the heap is a complete binary tree, while the physical structure is stored continuously in the array.

pile



1 heap structure

  The structure definition of the heap is composed of arrays, so it is necessary to dynamically open up space for the heap and the effective data and capacity of the heap.

typedef struct Heap
{
    
    
	HPDataType* a;
	int size; //有效数据个数
	int capacity; //容量
}HP; 

2 Heap initialization and expansion

  Now that the number of valid data has been saved, it will inevitably face the expansion problem caused by the full space.
Initialization: First, the space for the four plastics is not enough and then expand the capacity.

void HeapInit(HP* php)
{
    
    
	assert(php);
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);
	php->capacity = 4; 
	php->size = 0;
}

3 Heap fullness, size, heap top data and heap destruction

  It is relatively simple and directly on the code

HPDataType HeapTop(HP* php) //堆顶元素
{
    
    
	assert(php);
	assert(!HeapEmpty(php));
	return php->a[0];
}
bool HeapEmpty(HP* php) //判空
{
    
    
	assert(php);
	return php->size == 0;
}
int HeapSize(HP* php) //大小
{
    
    
	assert(php);
	return php->size;
}
void HeapDestroy(HP* php) //销毁
{
    
    
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}




Second, the insertion of the heap

  Insert in the heap, indicating that the previously inserted elements will form a heap. The nature of the heap must still be maintained after insertion, here we build a large heap. A large pile means that both parents are larger than the left and right children.

1 insert element

  Remember to check whether expansion is required before inserting elements

void HeapPush(HP* php, HPDataType x)
{
    
    
	assert(php);
	CheakCapacity(php);
	php->a[php->size] = x;
	AdjustUp(php->a, php->size);
	php->size++;
}

2 Adjust the build heap upwards

(1) Animation understanding

  Adjust upwards, if the inserted element is larger than its parents, you need to exchange their positions, and then iteratively adjust upwards until the child is smaller than the parents.


(2) Code implementation

void AdjustUp(HPDataType* a, int child)
{
    
    
	assert(a);
	int parent = (child - 1) / 2;
	while (child >= 0)
	{
    
    
		if (a[parent] < a[child])
		{
    
    
			Swap(&a[parent], &a[child]);
				//开始往上迭代
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
    
    
				//前提是前面的数据构成大堆
			break;
		}
	}
}




3. Deletion of the heap

  Before deleting the top element of the heap, first check whether the heap is empty and then adjust downwards. Here we have to think about why the elements at the top of the heap are deleted instead of the elements at the end of the heap. The elements at the end of the heap are easy to delete, but the practicability is not high. Delete the top of the heap The elements can solve TopK problems in life.

1 Why not delete the top data directly?

  After deleting the top element of the heap, when the left child is larger, there may be holes in the array, and it is not a complete binary tree.



  Here we learn a better method, first exchange the top element and the tail element of the heap, then the size of the heap – the last element cannot be accessed, and then adjust the restoration heap downward

void HeapPop(HP* php)
{
    
    
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&php->a[0], &php->a[php->size-1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

2 Adjust the undo heap down

(1) Animation understanding

(2) Code implementation

  To restore a large pile here, first find the larger one of the left and right children, and then iterate down. At the same time, pay attention to the details. You should first default that the left child is larger, because the right child may not exist, and directly setting the child may overflow. .

void AdjustDown(HPDataType* a, int size, int parent)
{
    
    
		//默认左孩子大,因为右孩子可能不存在就越界了
	int child = parent * 2 + 1;

	while (child < size)
	{
    
    
			//找到左右孩子中大的那一个
		if (child + 1 < size && a[child] < a[child + 1])
			child++;	//注意防止child+1>size

		if (a[parent] < a[child])
		{
    
    
			Swap(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
    
    
			//除了调整元素外其他元素构成堆
			break;
		}
	}
}




4. Heap sort

1 Think about why you build large heaps in ascending order and small heaps in descending order?

  If you want to arrange the array in ascending order by building a small heap, because the top element of the small heap is the minimum value, and the position of the top of the heap is also the position of the minimum value after sorting, which means that the top element of the heap does not need to be moved, then our heap needs Rebuild from the last bit, build a small heap, find the minimum value, and find the minimum value from the next bit until the entire array is arranged in ascending order. At first glance, there seems to be no problem, but the difference from building a large heap is that building The small heap needs to rebuild the heap from the next bit every time to select the most value. The complexity of this operation is O ( nlogn ) O(nlogn)O ( n l o g n ) ,compared to building a large heap, the time complexity isO ( logn ) O(logn)O ( log n ) is very full, so although small heaps can be built in ascending order, we choose to build large heaps because they are not as fast as building large heaps .
  The same is true for building small heaps in descending order.

2 heap builds

(1) Upward adjustment method O ( nlogn ) O(nlogn)O(nlogn)

  The time complexity of the upward adjustment method to imitate the insertion process of the heap is nlogn, here we assume that the heap has hhlayer h , k- thLayer k needs to be adjusted upwards by k − 1 k-1k1 time, then thekkthThe number of adjustments required for the k layer ( k − 1 ) ∗ 2 k − 1 (k-1)*2^{k-1}(k1)2k 1 = ( n − 1 ) ∗ 2 n − 1 a_n=(n-1)*2^{n-1}an=(n1)2n1


$S_n=2^0*0+2^1*1+……+2^{n-1}*(n-1)$ $2S_n=2^1*0+2^2*1+…+ 2^n*(n-1)$ is subtracted by dislocation: $S_n=2^n*(n-1)-(2^1+2^2+……+2^{n-1})=2 ^n*(n-2)+2))$ and $N=2^n-1$ all time complexity is $O(nlogn)$
void HeapSort(int* a, int size)
{
    
    
	for (int i = 1; i < size; i++)
	{
    
    
		AdjustUp(a, i);
	}
	int end = size - 1;
	while(end>0)
	{
    
    
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);
		--end;
	}
}



(2) The complexity of the upward adjustment method proves the second-level conclusion S n = ( A n + B ) ∗ qn − B ) S_n=(An+B)*q^nB)Sn=(An+B)qnB)

  In the above we already know that the nth layer an = ( n − 1 ) ∗ 2 n − 1 a_n=(n-1)*2^{n-1}an=(n1)2n 1 , and there is a secondary conclusion in high school, the sum of the first n items of this sequence must beS n = ( A n + B ) ∗ qn − B ) S_n=(An+B)*q^nB)Sn=(An+B)qnB ) , in this time we can findS 1 = a 1 = 0 , S 2 = 1 ∗ 2 1 = 2 S_1=a_1=0, S_2=1*2^1=2S1=a1=0,S2=121=2
S 1 = ( A + B ) ∗ 2 1 − B = 2 A + B = 0 S_1=(A+B)*2^1-B=2A+B=0 S1=(A+B)21B=2A _+B=0
S 2 = ( 2 A + B ) ∗ 2 2 − B = 8 A + 3 B = 2 S_2=(2A+B)*2^2-B=8A+3B=2 S2=( 2A _+B)22B=8A+3B _=2Simultaneous
two formulas getA = 1 , B = − 2 , so S n = ( n − 2 ) ∗ 2 n + 2 A=1, B=-2, so S_n=(n-2)*2^n +2A=1,B=2,Therefore Sn=(n2)2n+2
here the time complexity is O(nlogn) obviously here the time complexity is O(nlogn) obviouslyHere the time complexity is O ( n log n ) obviously


(3) Downward adjustment method O ( N ) O(N)O ( N )

  The downward adjustment method starts from a non-leaf node in the reciprocal of the heap, and the time complexity must be less than O ( nlogn ) O(nlogn)O ( n log n ) . _ _ Here we set the heap to havehhh layer

S n = 2 0 ∗ ( h − 1 ) + 2 1 ∗ ( h − 2 ) + … … + 2 h − 2 ∗ 1 S_n=2^0*(h-1)+2^1*(h -2)+...+2^{h-2}*1Sn=20(h1)+21(h2)+……+2h21
2 S n = 2 1 ∗ ( h − 1 ) + 2 2 ∗ ( h − 2 ) + … … + 2 h − 1 ∗ 1 2S_n=2^1*(h-1) + 2^2*(h-2)+……+2^{h-1}*1 2Sn=21(h1)+22(h2)+……+2h11
dislocation subtraction:
S n = 2 1 + 2 2 + 2 ( h − 2 ) + 2 h − 1 − 1 + h = 2 h − 1 + 1 − h = 2 h − h S_n=2^1+ 2^2+2^{(h-2)}+2^{h-1}-1+h=2^h-1+1-h=2^hhSn=21+22+2(h2)+2h11+h=2h1+1h=2hh
and 2 h − 1 = N, then the time complexity is O ( N ) and 2^h-1=N, then the time complexity is O(N)Another 2h1=N then the time complexity is O ( N )

void HeapSort(int* a, int size)
{
    
    
		//从倒数第一个叶子结点开始向下调整堆
	for (int i = (size - 1 - 1) / 2; i >= 0; i--)
	{
    
    
		AdjustDown(a, size, i);
	}
		//每次把堆首尾交换,大的数字在最后面,在调回大堆
	int end = size - 1;
	while(end>0)
	{
    
    
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);
		--end;
	}
}

3 Imitate heap deletion for sorting

  Here we imitate the deletion of the heap, change the first and last elements of the heap, and arrange a number at a time. Just adjust the heap, and the code has been attached to the heap adjustment.

Summarize

  This is the end of the content of the heap. I hope that the second-level conclusion to find the time complexity of the upward adjustment of the heap will be helpful to you . I look forward to seeing you in the next blog!

Guess you like

Origin blog.csdn.net/Front123456/article/details/129889633