【数据结构初阶】简析堆排序

本文详细介绍了堆排序算法,包括建堆方法的比较、堆排序的具体实现步骤,并提供了代码示例。通过对向下调整建堆和向上调整建堆的时间复杂度分析,展示了向下调整建堆的优势。


目录

🌳一、引言

🌳二、建堆方法的比较

🌳三、堆排序

🌵1.建堆

🌵2.利用堆删除思想进行排序

🌳四、总结

🌵1.向下调整建堆(升序->大堆,降序->小堆)

🌵2.头尾数交换,向下调整


一、引言

通过之前的学习,让我们对堆的概念有了大致的了解, 

想了解 堆 基础知识的同学看这里: 二叉树(堆)基础知识

今天就让我们一起来学习一下堆排序的内容:

堆排序相较于我们以前学过的C语言冒泡排序,它的效率达到了基于比较的排序算法效率的峰值(时间复杂度为O(nlogn)),具有显著优势

堆排序即利用堆的思想来进行排序


二、建堆方法的比较

我们都知道堆排序是以堆的逻辑结构为基础的,建堆的方法通常有两种

  • 向上调整建堆
  • 向下调整建堆

向上调整建堆,每插入一个数,向上调整一次(也就是说,要从根节点开始,遍历所有节点)

向下调整建堆,从倒数第一个非叶子节点(最后一个节点的父亲)开始调整,逐个向下调整,只需要遍历除叶子节点以外的节点就可以了

因此,我们通常会使用向下调整建堆,因为向下调整建堆的时间复杂度更小

为了更好的说明二者的差别,我们来看一组对比图:

向下调整建堆时间复杂度: 

 向上调整建堆时间复杂度(以最后一层为例):

 

即使是最后一层调整,它的时间复杂度也已经达到(n+1)*(log(n+1))/2,最终全部层数加起来,时间复杂度的级别应为 n*logn 级别

对比向下调整的 n 级别,易得向下调整建堆更具有优势。

【注意】建好堆之后并不相当于有序


三、堆排序

1.建堆

  • 升序:建大堆
  • 降序:建小堆

为什么要这样建堆呢?一是方便选数,二是避免父子大小关系混乱(第一个最小数排好后,如果把剩下的数看作堆,那么父子关系全乱了)

同时我们也可以从时间复杂度上来思考:

升序:建大堆(N*logN —— N个数,排logN次)

           建小堆(N*N —— N个数,排N次)

2.利用堆删除思想进行排序

我们这里以 升序->建大堆 为例 

void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1;
	while (minChild < n)
	{
		// 找出大的那个孩子
		if (minChild + 1 < n && a[minChild + 1] > a[minChild])
		{
			minChild++;
		}

		if (a[minChild] > a[parent])
		{
			Swap(&a[minChild], &a[parent]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	// 大思路:建堆,依次选数,从后往前排
	// 升序 -- 大堆
	// 降序 -- 小堆
	// 建堆 -- 向下调整建堆 - O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	// 选数 
	int i = 1;
	while (i < n)
	{
		Swap(&a[0], &a[n - i]);//交换第一个和最后一个
		AdjustDown(a, n - i, 0);//数组地址+尾部处+开始处
		++i;
	}
}
int main()
{
	int a[] = { 15, 1, 19, 25, 8, 34, 65, 4, 27, 7 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

 降序->建小堆 的方法也类似,这里就不在多赘述了,有兴趣的小伙伴可以自己尝试一下

 【理解】升序:大的在叶子节点;降序:小的在叶子节点


四、总结

堆排序方法:

1.向下调整建堆(升序->大堆,降序->小堆)

2.头尾数交换,向下调整

评论 15
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值