堆排序(附降序代码)

在堆的代码上基础上写本篇文章,堆的代码见:树,二叉树与堆

之前写的堆并不能说堆排序,只是堆的实现,之所以这样说是因为之前有建堆的过程 ,新开辟了一份空间,之前的数据并没有改变。

在原数据上进行建成堆

向上调整建堆

如何只在原来数据上进行堆排序:

直接模拟堆插入的过程,然后向上调整。之前插入新的数据才向上调整,但是函数的参数只是数组的下表,有了数组下标就可以调整,所以直接调整数组的数据也没有问题。
时间复杂度:O(n*logn)

void HeapSort(HPDatetype* p, int n)
{
	assert(p);
	for (int i = 1; i < n; i++)//i = 1,从第二个数据开始向上调整
	{
		AdjustUP(p, i);
	}
}

主函数:

void test3()
{
	int arr[] = { 4, 6, 2, 1, 5, 8, 2, 9 };
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	HeapSort(arr, sizeof(arr) / sizeof(arr[1]));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	test3();	
	return 0;
}

向下调整建堆

向上调整建堆:从第二个子结点开始一直和上面的比较,每个节点都要比较至根结点。
向下调整建堆:从倒数第一个非叶子节点(最后一个叶子结点的父结点)开始向下比较,每一个数都要比较到最后一个。

时间复杂度比较:

调整建堆:向上调整建堆和之前开辟空间建堆的时间复杂度是一样的,二着本质都是插入建堆,只是一个开辟了空间,一个没有开辟。具体可见中最后的时间复杂度分析

调整建堆:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因此:建堆的时间复杂度为O(N)。

对比可以发现,向上调整是越往后每一层数据越多,向上调整的高度也变大,而向下调整是越往上每一层数据越来越少,调整的高度变多。明显向下调整时间复杂度更低。
总结就是向上的是结点多的调整次数多节点少的调整次数少
向下的是节点多的调整次数少,结点少的调整次数多。

同样对于一个无序的数组,只需要向下调整即可建立堆

void HeapSort(HPDatetype* p, int n)//不开辟空间,在原数组上进行堆排序
{
	assert(p);
	/*for (int i = 1; i < n; i++)
	{
		AdjustUP(p, i);
	}*/
	for (int i = (n-1 - 1) / 2; i >= 0; --i)//n - 1是最后一个结点的下标,再减一除2就是最后一个非叶子结点
	{
		AdjustDown(p, n, i);
	}
}

对数据进行升序排列

对一组数据进行降序排列,如果使用传统的遍历,那么时间复杂度很高比如冒泡排序为O(n^2),这是可以利用堆的性质进行排序,时间复杂度为O(n*logn)。

建立大堆,此时逻辑上是有序的大堆,但是物理结构上不是,而且也不是升序排列的数据。之所以建立大堆,是因为每次堆顶都是最大的数,选择最大的数以后,根节点的子树还是大堆,调整数据时就会很方便,如果建立小堆,那么只能选出最小的那个数,再向下选择会越往下越麻烦,数据量会越来越大。选择前两个小数好选,在第三个数往后就不好选了 ,比较数的大小时,要么不停的建立堆,进行查找最小的数,要么直接比较,但是在高度为3往后在比较就很难比较了,而且越往后,执行的次数越多,要不停的遍历,效率很低。
如果选择一个最小的数就建立一次堆,时间复杂度就会很高。所以建立一个大堆,选择最大的数,然后选择次大的数,不断的沉到最后面。这样就是一个升序的数组了。之前想过可以建立小堆进行排序,也是首尾交换,但是放在数组里面就算是降序了,所以这种方式适合降序排列数组的时候使用。

实现逻辑:

先建立一个大堆,此时最大的数在上面,而且堆的逻辑结构清晰,但是无法找到升序的排列。
所以将最大的数和最后一个数交换,此时最大的数在队尾,但是只有根节点的子树是大堆,所以让堆顶的数向下调整。使整个堆变为大堆。此时次大的数在堆顶
然后队尾舍弃掉刚刚最大的数,队尾变为刚刚数的前一个。只是调整时逻辑位置舍弃了,实际上还存在。
如此循环,让最大的数不停的去到队尾,最后就得到了升序排列的数

降序只需要将大堆换成小堆就行,逻辑一样。

降序代码为例:

void HeapSort(HPDatetype* p, int n)//不开辟空间,在原数组上进行堆排序
{
	assert(p);
	for (int i = 1; i < n; i++)
	{
		AdjustUP(p, i);
	}
	int end = n - 1;//end是数组下标,n是数据个数
	while (end > 0)
	{
		Swap(&p[0], &p[end]);
		AdjustDown(p, end, 0);//第二个参数是数据个数,少了队尾数据个数就是队尾的数据下标
		--end;
	}
}

主函数:

#include "Heap.h"
void test3()//堆排序
{
	int arr[] = { 4, 6, 2, 1, 5, 8, 2, 9 };
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	HeapSort(arr, sizeof(arr) / sizeof(arr[1]));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	test3();	
	return 0;
}

如此就实现了数据的降序排列

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值