堆排序

《算法导论》一书中提到几个经典的排序算法,今天所写的是我所理解到的堆排序,如果有误,恳请大神提出修改意见。

堆排序(Heapsort)是指利用这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

——来自维基百科

堆排序:由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。它是对数据的有序性不敏感的一种算法。但堆排序将需要做两个步骤:-是建堆,二是排序(调整堆)。所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能。

     ——来自百度文库

理解各种排序的优缺点对于排序的学习有很大的帮助哦。下面我将以题目的形式来讲述堆排序。

1.如果有一个数组,假设是p{3,6,9,0,20,0,100,1,2,100},是以0为起始(有些书是以1起始的,所以有所不同)。我们可以把这个数组想象成一个完全二叉树(堆排序经常结合完全二叉树理解,但实际上没有建树,直接在数组上操作)。

             

其中3在数组的下标是0,6是1,9是2,依次类推。

再明确一个重要的关系,就是3的左右孩子是6和9,对应的下标是1和2,也就是0*1+1和0*1+2。归纳可以知道给出一个下标i,它的左孩子是2*i+1,右孩子是2*i+2(假设均不超出数组的范围)

2.有了上面的思路,接下来就是建堆的过程。

   1).首先从上面的树看,建堆只需要从20(下标为4)开始,而不是100(下标为9),原因是20为最后一个父亲节点了。所以先找到最后一个父亲节点(p.length/2-1),从此处开始执行维护堆的操作,一直回到根节点。C代码如下:

void BuildMaxHeap(int *p,int length)
{
int i;
for(i=length/2-1;i>=0;i--){
MaxHeapify(p,i,length);  //该方法是维护堆操作
}
}


2).在此列举几步建堆的操作,过程是类似的,可以举一反三。注意到堆的特点子结点的键值或索引总是小于(或者大于)它的父节点,在此所建的堆是子结点的键值总是小于它的父节点,也就是我们常说的大顶堆。

2.1.由于20小于它的左孩子100,那么就把20与100交换位置,然后20就去到数组的最后,不需要继续执行维护堆的操作。见下图:

2.2.接着下标退回到3,也就是0执行维护堆的操作,比较发现右孩子2比左孩子1大,所以0和2交换。见下图:

                   

              2.3.跳过下标为2的操作(与前面的操作一样),直接去到下标为1的操作。由于6比100小,所以6和100交换,又由于交换完之后6还是有左孩子20,继续比较,6和20交换。如下图:

                 

              2.4.最终的建堆结果如下:

                

维护堆的操作函数为:

void MaxHeapify(int *p,int i,int length)
{
int l = Left(i);                   //获取左孩子下标
int r = Right(i);                //获取右孩子下标
int largest = i;        
if(l<=length-1&&l>=0&&p[l]>p[i]){
largest = l;              //父亲比左孩子小,记下左孩子下标,以便交换
}
if(r<=length-1&&r>=0&&p[r]>p[largest]){
largest = r;
}
if(largest != i){              //如果不相等,要进行交换操作
int temp = p[i];
p[i] = p[largest];
p[largest] = temp;
MaxHeapify(p,largest,length);        //继续执行维护堆的操作,知道左右孩子下标都超过了数组的范围
}
}


3.建好堆之后,还需要进行的步骤是排序(建好的堆并没有按照从大到小的顺序排好)。

回到数组讲述,第一步首先将怕p[0]和p[9]交换,因为p[0]肯定是最大的那个数了,所以将其放到最后,接下来对前面的8个数进行维护堆的操作,也就是重新建成一个小一点的堆,直到根节点结束。代码如下:

void Heapsort(int *p,int length)
{
int i;
int psize = length - 1 ;
for(i=length-1;i>=1;i--){
int temp = p[i];
p[i] = p[0];
p[0] = temp;
MaxHeapify(p,0,psize--);
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值