数据结构--排序之堆排序

●个人主页:你帅你先说.
●欢迎点赞关注收藏
●既选择了远方,便只顾风雨兼程。
●欢迎大家有问题随时私信我!
●版权:本文由[你帅你先说.]原创,CSDN首发,侵权必究。

堆排序基本思想及其代码实现

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

前面我们已经学过了堆的各种实现,现在我们要基于堆的各种操作来实现一个堆排序。
假设我们排的是升序,基于前面TopK的思想,我们可以先建一个小堆,然后取堆顶,然后删除堆顶,调堆,一直重复这样下去,Popn次就可以得到最终结果。
代码实现:

void HeapSort(int* a, int n)
{
	HP hp;
	HeapInit(&hp);
	// 建一个N个数的小堆
	for (int i = 0; i < n; ++i)
	{
		HeapPush(&hp, a[i]);
	}
	// Pop N 次
	for (int i = 0; i < n; ++i)
	{
		a[i] = HeapTop(&hp);
		HeapPop(&hp);
	}
	HeapDestroy(&hp);
}

各种接口的细节->数据结构—二叉树、堆
这样实现确实可以,没毛病,但如果你要排一个非常大的数组,就要消耗很多的空间,能不能想一个方法把空间复杂度优化到O(1)。
我们可以直接在原数组上进行操作,这样就不用消耗额外的空间。
首先,我们先把数组调整成一个小堆

//法一:向上调整
for(i = 1;i < n;i++)
{
	AdjustUp(a,i);
}
//法二:向下调整
for (i = (n - 1 - 1)/2; i >= 0; i--)
{
	AdjustDown(a,n,i);
}

:向下调整的使用前提是:根结点的左右子树都为小堆,所以向下调整需要做一些改变,我们从倒数第一个非叶子结点的子树开始调小堆。

调成小堆后,堆顶元素就是最小的,接下来我们就是选出次小的数,选出次小的数我们需要把剩下的结点重新建成一个小堆,这个时候问题就来了,建一个堆的时间复杂度为O(N)要建N次,时间复杂度为O( N 2 N^2 N2),那我们这个排序的效率就太低了,体现不出它的优势。

那我们换种思路,如果我们建大堆呢?我们可以利用删除堆顶元素的思想,每次调成大堆后就与最后一个元素进行交换,这样最大的数就放在了最后一个,然后再次调堆,把次大的数放在倒数第二个…一直这样下去。
上述过程用动图演示:
数据结构--排序之堆排序_第1张图片

void HeapSort(int* a, int n)
{
	// O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	// 依次选数,调堆
	// O(N*logN)
	for (int end = n - 1; end > 0; end--)
	{
		Swap(&a[end], &a[0]);

		// 再调堆,选出次小的数
		AdjustDown(a, end, 0);
	}
}

堆排序的时间复杂度为O(n l o g 2 n log_2n log2n)

觉得写的不错可以给个一键三连 点赞关注收藏

你可能感兴趣的:(数据结构,数据结构,算法,排序算法)