《数据结构》(10) - 排序算法(2)- 堆排序

文章目录


提示:以下排序均以升序排列为例

堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过建堆来进行选择数据。
堆
 要使用堆排序,首先要学习堆的向下调整算法,因为要使用堆排序,必须先建堆,而建堆的次数可以通过堆的向下调整算法得出。

堆的向下调整算法(使用前提):
 1.若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
 2.若想将其调整为大堆,那么根结点的左右子树必须都为大堆。
在这里插入图片描述
堆的两个特性
 1. 结构性:用数组表示的完全二叉树
 2. 有序性: 任一结点的值是其子树的最大值(最小值)。

在这里插入图片描述

向下调整算法的基本思想(以建小堆为例):

从根结点处开始,选出左右孩子中小的那个与父亲进行比较,如果比父亲小就交换(如果不比父亲小就结束),然后继续向下调整,直到调整到叶子结点为止。
在这里插入图片描述

//堆的向下调整算法
void AdjustDwon(int* a, int n, int root)
{
	int parent = root;
	int child = parent * 2 + 1; // 默认是左孩子
	while (child < n)
	{
		// 1、选出左右孩子中大的那一个
		if (child + 1 < n && a[child+1] > a[child])
		{
			child += 1;
		}
		
		//与父亲比大小
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

上面说到,使用堆的向下调整算法的前提是根结点的左右子树为大堆或是小堆,但我们并不能保证我们要排序的序列都满足这个条件,如果左右子树都不是小堆,此时不能直接使用向下调整算法了,怎么办?

实际上我们只需要从倒数第一个非叶子结点的子树开始,从后往前,按下标,依次作为根去向下调整即可。

	// 建堆  O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

那么我们应该建大堆还是建小堆呢?

如果是建小堆,最小数在堆顶,最小数选出后,剩下的数中再去选数,但是剩下的树结构都乱了,只能重新建树,才能选出下一个数,建队的时间复杂度为O(N),不合理,应该建大堆。

void HeapSort(int* a, int n)
{
	// 建堆  O(N)
	//排升序,建大堆
	//从第一个非叶子结点开始向下调整,一直到根
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

	// 排升序,建大堆还是小堆?建大堆
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);//将堆顶的数据和堆的最后一个数据交换
		AdjustDwon(a, end, 0);
		--end;
	}
}

建堆的时间复杂度为O(N);

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值