数据结构与算法_堆排序

堆排序是一种效率为O(N*logN)的排序算法,优于O(N^2)的冒泡排序。建堆过程包括向上调整和向下调整,用于创建大堆或小堆。在排序过程中,先构建堆,然后交换堆顶元素与末尾元素并重新调整堆,重复此过程直到排序完成。代码实现中包含了向上调整和向下调整的函数,以及完整的堆排序函数。
摘要由CSDN通过智能技术生成

堆排序,即利用堆的思想来进行排序。要实现堆排序,首先要建堆,建堆又分为建大堆和建小堆;然后再一步一步地删除堆的元素来进行排序。

目录

一、堆排序的时间复杂度

二、建堆

向上调整

向下调整

三、堆排序

四、代码实现

向上调整

向下调整

堆排序


一、堆排序的时间复杂度

相比冒泡排序,它的时间复杂度为O(N^2),堆排序的时间复杂度为O(N*logN)。优点就在此。

假如现在要分别对1000个和1000000个数据进行排序,那么

N冒泡排序堆排序
1000100000010000
10000001E+1220000000

可见,堆排序的次数更少,更能有效节省时间。

二、建堆

建堆有两种,分别是向上建堆和向下建堆,应该根据需求建大堆或是小堆。

向上调整

以建小堆为例,2是新插入的数据,让他和父节点5比较,发现父节点比它大,就交换。

然后再和新的父节点1比较,发现新的父节点比它小,就不需要再进行交换了。

再以建大堆为例,这是一个无序的数组:

它们的逻辑结构如下:

现对它进行向上调整,建大堆,通过调试可以发现,数组元素的值有所改变:

最终,它的逻辑结构如下:

以建小堆为例,向上调整思想如下:

①将要插入的数据与其父节点数据比较

②若子节点数据小于父节点数据,则交换

若子节点数据大于父节点数据,则不交换,不需要调整,已经满足堆的结构

建大堆只是比较大小有所不同。

向下调整

向下调整的一个重要前提是左右子树必须是堆,如图所示:

 以建小堆为例:

 首先让父节点27和第一个子节点比较大小,发现父节点比子节点大,那么两者交换;

让27和它的第一个子节点比较,发现父节点又比子节点大,那么两者再次交换;

27和它的第一个子节点比较大小,发现第一个子节点比父节点大,那么不交换,让父节点和第二个子节点比较大小,发现父节点比第二个子节点大,那么让两者进行交换。

到了叶子节点了,叶子节点没有子节点,停止比较。最终得到了小堆。

以建成小堆为例,向下调整的思想如下:

①从根节点开始,选出左右孩子节点中值较小的一个

②让父亲与较小的孩子比较

若父亲大于此孩子,那么交换

若父亲小于此孩子,则不交换

③结束条件

1、父亲<=小的孩子则停止

2、调整到叶子节点,(叶子节点特征为没有左孩子,就是数组下标超出了范围,就不存在了)

三、堆排序

堆排序思想如下:

以升序为例,堆排序的思想如下:

①将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。

②将其与末尾元素进行交换,此时末尾就为最大值。

③然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

四、代码实现

向上调整

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

向下调整

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

不论是向上调整还是向下调整,要想建立大堆或小堆,关键在父节点和子节点比较时的符号,可自行修改。

堆排序

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

	// 建堆 -- 向下调整建堆 -- O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);
		--end;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值