【算法设计】堆排序

一、概述

有两种堆,分别为最大堆与最小堆。顾名思义,最大堆的堆顶为最大的元素,最小堆为最小的元素。即

最大堆:

ki>=k2i&& ki>=k2i+1


二、算法实现

1.基本算法

以下以最大堆为例,算法如下:

⑴假设输出堆顶元素后,将新元素e置于堆顶

⑵取其左右儿子中最大者s与新元素比较

⑶由于左右两棵子树本就是最大堆,

  若e>=s,则重新成为最大堆;

  否则,将s与e调换位置。此时,将会破坏最大儿子所在的堆

⑷继续调整


代码实现如下:

void HeapAdjust2(int data[],int index,int length){
	for(int i=index;2*i+1<length;){
		int k=2*i+1;				
		if(k<length-1&&(data[k]<data[k+1])) k++; //选择大的孩子,注意并非都有右孩子
		if(data[i]<data[k]){					 //将需要调整的元素e与最大儿子进行交换
			int temp=data[i];
			data[i]=data[k];
			data[k]=temp;
			i=k;								 //元素e调整到了新位置
		}
		else break;
	}


}


2.简单改进

在以上实现中,需要调整的元素多次进行了交换才到了最终确定的位置,在这其中多次交换了又多次被覆盖,是为重复无用的操作。因此做出以下改进无需进行交换:

void HeapAdjust(int data[],int index,int length){
	int rs=data[index];
	for(int i=index;2*i+1<length;i++){
		//最大堆
		int k=2*i+1; //左孩子下标
		if(k<length-1&&(data[k]<data[k+1])) k++;  //选择大的孩子,注意并非都有右孩子
		if(data[index]>=data[k])break;
		data[index]=data[k];
		index=k;
	}
	data[index]=rs;
}

注意

1.      堆排序仅占用一个记录大小的辅助空间,每个待排序记录占一个存储空间,空间复杂度为O(1),时间复杂度为O(nlogn)

2.      注意并非完全二叉树,非终端结点可能只具有左儿子。

3.      从无序序列进行建堆时,只需从最后一个非终端结点开始即可,即从floor(length/2)开始


3.应用

首先是应用在堆排序中,如下:

void HeapSort(int data[],int length){
	for(int i=length/2-1;i>=0;i--)  //从最后一个非终端结点开始建堆
		HeapAdjust(data,i,length);
	for(i=length-1;i>0;i--){        //将当前堆顶(最大元素)和未排序的最后一个元素交换后再调整。即得到升序序列
		int temp=data[i];
		data[i]=data[0];
		data[0]=temp;
		HeapAdjust(data,0,i-1);
	}

}


其次,对于大量数据里求最大或最小的N个数问题,用堆也是个绝佳的选择。

以在海量数据里求最小的n个数为例,可使用最大堆。这是因为:在n个数据的堆里,堆顶为其中最大的元素。对新的一个元素,将其与堆顶元素相比,若大于堆顶,则该数必定不在最小的n个数里;若小于堆顶,则将堆顶抛弃,加入新元素再重新调整成新的堆,从而就保证有n个数的堆,且堆顶是n个数里最大的元素。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值