introduction to algorithms sorting lesson_5 Heap sorting(堆排序)

  与前面介绍的几种排序方式不同,Heap sort 堆排序是一种原地排序算法,不需要像合并(归并)排序那样要额外的储存空间

  其运行时间为 O(nlogn) ,与合并排序相同。

 对堆的简单介绍:堆一般为二叉堆是一种数据结构


最小堆:



在数组中,由于下标从0开始,则根i的左右节点分别为2i+1 和2i+2

堆在数组中的储存一般为:从上到下,从左到右,因为二叉堆都是完全二叉树,

则在有n个节点的堆中,n/2……n均为叶子节点,这一性质在后面的堆排序中至关重要


堆排序基本思想:

一、建立一个最大堆 保证首元素为最大元素

二、将首元素data[0]与最末尾元素data[n](较小)的交换,则末尾元素data[n]成为最大元素。

三、此时,最大堆性质被破坏,重新调整data[0……n-1],使其重新成为一个最大堆,

则data[0]又成为最大元素,再将data[0] 与data[n-1]交换,以此类推,交换到data[1]时即可停止循环


因此要解决三个问题:1是如何调整堆,2是如何建一个最大堆,3是如何循环交换调整

先来看第一个问题:如何调整堆

调整堆就是用根节点与其左右孩子节点比较,将较大的值上浮,较小的值下降



1 较小则与最大的 3 交换,由于交换后 右孩子从3变为1,不能保证右孩子为根节点的树为最大堆,则继续调整右孩子,直到叶子节点结束,这样就保持了堆的性质

这样自然而然就产生了递归的方法与结果

代码为:MaxHeapfiy函数实现

/***********Heap  sorting**************/
#define  leftchild(i)  (2*i+1)  //下标从0开始 左孩子
#define  rightchild(i)  (2*i+2) //下标从0开始 右孩子
void max_Heapify(int data[],int heapsize,int i) //保持最大堆性质 i为最大堆根节点
{
	int l=leftchild(i);
	int r=rightchild(i);
	int largest;
	if (l<=heapsize&&data[i]<data[l])        //比较树节点
		largest=l;
	else
		largest=i;
	if (r<=heapsize&&data[largest]<data[r])
	    largest=r;
	if (largest!=i)                         //不为最大堆 则交换值
	{
		int tmp=data[largest];
		data[largest]=data[i];               
		data[i]=tmp;                        //此时最大堆性质可能破坏
		max_Heapify(data,heapsize,largest);  //递归调用调整交换后的树节点,保持最大堆性质
	}
}

再来看第二个问题:如何建立一个最大堆

这就要用到第一个函数maxHeapfiy,从前面的结束可知,madHeapfiy是从根节点往下调整的,并不能用max_Heapfiy(data,heapsize,0)建立一个最大堆,

因为后面的元素可能比根节点要大,max_Heapfiy只是起到将较小的值下降的作用,使较大的值上浮一次,但不能让最大的值上浮达根节点

前面已经说过 ,二叉堆是一个完全二叉树,在有n个节点的堆中,n/2……n均为叶子节点,剩余节点均不为叶子节点

由此我们就用这种思路,若用由底层向上的节点顺序调用max_Heapfiy函数,即  i 以 n/2……0的顺序调用max_Heapfiy

便可以生成一个最大堆

代码:buildMaxHeap

void buildMaxHeap(int data[],int len)      //建造最大堆
{
	for (int i=len/2;i>=0;i--)            //data[LEN/2……LEN]均为叶子节点	                                 
		max_Heapify(data,len,i);          //对剩余节点从底向上调整(最后是DATA[0]),则可得到最大堆
}

运行结果如图所示:data [ ]={1,0,2,9,3,1,5,8,9,10}



可以看到运行后最大堆已经建立啦,但原数组并没有排好序,只是使得首元素为最大元素10

最后让我们轻松地解决第三个问题:完成堆排序

直接贴代码:

/********堆排序算法,非递减排序*********/
void heapsort(int data[],int len)       
{
	buildMaxHeap(data,len);       //构造最大堆
	int heapsize=len;
	for (int i=len;i>0;i--)      //data[0]为每次调整后最大元素,与末尾元素data[i]交换
	{
		int tmp=data[i];
		data[i]=data[0];
		data[0]=tmp;
		heapsize--;
		max_Heapify(data,heapsize,0);//调整,使data[0]为最大元素
	}
}

测试:


#define getArraySize(arrayName) (sizeof(arrayName)/sizeof(arrayName[0])) //获取数组长度
int _tmain(int argc, _TCHAR* argv[])
{
	int data_test[10]={1,0,2,9,3,1,5,8,9,10};
	prtarray(data_test,getArraySize(data_test));//打印原数组
	heapsort(data_test,getArraySize(data_test)-1);//堆排序
	printBinTree(data_test,getArraySize(data_test));//以二叉树的形式打印数组
	prtarray(data_test,getArraySize(data_test));//打印排好序的数组
	system("pause");
	return 0;
}

运行结果:



算法分析:

每次调用max_Heapfiy的运行时间为O(logn),即树的深度 然后又O(n)次调用max_Heapfiy

故堆排序的时间复杂度为 O(logn) *O(n) =  O(n *logn)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值