堆排序算法详解

目标:将数组按从小到大排列

算法描述:堆排序中,将待排序数组看成一颗完全二叉树,待排序数存放在A[1...n],不是从A[0]开始,因为要利用完全二叉树中节点编号之间的关系:下标为i的节点的左右孩子节点下标为2i和2i+1。对于大根堆A[i]>=A[2i]且A[i]>=A[2i+1],最大元素存放在根节点中(对于小根堆A[i]<=A[2i]且A[i]<=A[2i+1],最小元素存放在根节点中)。堆排序中,首先需要建立初始堆,然后将堆顶元素A[1](n个元素中的最大值)与堆底元素A[n]交换,对剩下的A[1...n-1]进行调整,使之重新成为一个大根堆,再将堆顶元素A[1](n-1个元素中的最大值)与堆底元素A[n-1]交换,如此重复,直到堆中只剩下一个元素为止。

算法实现(已经过测试运行):

/*函数功能:堆排序
**函数参数:A为数组,len为堆节点个数,即数组A的长度减1
*/
void HeapSort(int A[], int len)
{
	BuildMaxHeap(A, len); //初始建堆
	for (int i = 0; i < 8; i++){ cout << A[i] << endl; }
	for (int i = len; i>1; i--)
	{
		int tmp = A[i];
		A[i] = A[1];
		A[1] = tmp;

		AdjustDown(A, 1, i - 1); //从A[1]开始向下调整,把剩余的i-1个元素整理为大根堆
	}
}

/*函数功能:建立大根堆
*/
void BuildMaxHeap(int A[], int len)
{
	for (int i = len / 2; i>0; i--)  //从len/2开始调整小的大根堆,一直调整到A[1]
	{
		AdjustDown(A, i, len);
	}
}

/*函数功能:向下调整,将下标为i的元素以及其所有子孙建立成一个大根堆
**函数参数:A为数组,i为待调整元素下标
*/
void AdjustDown(int A[], int i, int len)
{
	A[0] = A[i];  //暂存
	for (int j = 2 * i; j <= len;j*=2)
	{
		if (j<len && A[j]<A[j + 1])
			j++;      //j记录值较大的左右孩子的下标
		if (A[0] >= A[j])
			break;    //父节点本来就比孩子节点值都大,不用调整
		else
		{
			A[i] = A[j];
			i = j;
		}
	}
	A[i] = A[0]; //被筛选节点的值最终放置位置为i
}



算法性能

时间复杂度:最好情况,O(nlogn)

                        最坏情况,O(nlogn)

                        平均情况,O(nlogn)

初始建堆的时间复杂度:O(n)

空间复杂度:仅使用了常数个辅助单元,O(1)

稳定性:不稳定


拓展:

在1000W个数中找出最小的1000个数,建立1000个节点的大根堆。当第1001个数出现,首先与堆顶(最大元素)比较,如果比堆顶还大,则第1001个数必然不在最小的1000个数之列,丢弃之;如果比堆顶小,则替换堆顶元素,并向下调整,使之重新成为一个大根堆。直到处理完1000W个数。


同理,如果要在1000W个数中找出最大的1000个数,则需要建立1000个节点的小根堆



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值