【C语言数据结构】堆排序

给定一个有n个数据的数组,将其排列为升序或者降序。

//这里是下面用的到的升降序排序函数代码
//向下排序
void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
		//                    如果建立小堆这里应变为>
			child++;
		if (a[child] > a[parent])
		//     如果建立小堆这里应变为<
		{
			int tmp = a[parent];
			a[parent] = a[child];
			a[child] = tmp;

			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
//向上排序
void AdjustUp(int* hp, HeapDataType child)
{
	assert(hp);
	int parents = (child - 1) / 2;
	while (child)
	{
		if (hp[child] > hp[parents])
		//  如果建立小堆这里应变为<
		{
			HeapDataType tmp = hp[parents];
			hp[parents] = hp[child];
			hp[child] = tmp;

			child = parents;
			parents = (child - 1) / 2;
		}
		else
			break;
	}
}

这里以升序为例
1.建小堆,排升序。

①将数组的第一个元素看做堆,后面的数据依次加上堆,然后用向上调整构造小堆。

void AdjustUp(int* a, int n)
{
	assert(a);
	int i = 0;
	for (i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
}

②倒着走找第一个父节点,然后对这个父节点向下调整,保证它下面的子树都是小堆。
叶节点的子树不需要调整,因为他本身就是小堆;所以从倒着走的第一个非叶节点的子树开始调整。

void AdjustUp(int* a, int n)
{
	assert(a);
	//找出第一个父节点
	int i = (n - 1 - 1) / 2;
	for (int k = i; k >= 0; k--)
	{
		AdjustDown(a, n, k);
	}
}

但是这两种方法只可以将当前的数组列为小堆,只能选出最小的数,而次小的数则找不出来。从第二个位置开始看做堆,但这之前建立的关系全都乱了,只能重新建堆,才能找出次小的数,这样一来很麻烦。所以我们可以换一种思路。

2.排升序,建大堆
①利用上两个方法将当前的数组建成大堆,选出最大的数
②最大的数跟最后一个数交换
③最后一个数不看做堆内元素进行向下调整,即可选出次大的数
以此类推重复,时间复杂度为NlogN

注意:这里的AdjustUp和AdjustDown已经和上面的不同,父子节点关系已经改变

void HeapSort(int* a, int n)
{
	assert(a);
	//建立大堆
	int i = (n - 1 - 1) / 2;
	for (int k = i; k >= 0; k--)
	{
		AdjustDown(a, n, k);
	}
	for (int i = n - 1; i > 0; i--)
	{
	    //将第一个元素和最后一个互换
		int tmp = a[0];
		a[0] = a[i];
		a[i] = tmp;
		//不看最后一个元素向下调整
		AdjustDown(a, i, 0);
		//注意此处i既是最后一个元素的下标,又是删除最后一个元素后前面元
		//素的个数,所以传i就代表了不看最后一个元素
	}
}

如果要排成降序,那么只需要改变AdjustDown和Adjustup内父子元素的关系即可。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值