堆排算法的分析与总结

  关于对的算法思想,随处都可以查到,下面总结下堆的算法。

  堆排序和直接选择排序都是选择排序,即从未序区间中选择最大或者最小的元素追加到已序区间的尾部,直到剩下一个元素。

  直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。

  堆排序可通过树形结构保存部分比较结果,可减少比较次数。

  堆排序算法的解析:

  1、建初始堆(以大顶堆为例),这一步骤把初始序列建成了一个满足大顶堆性质的序列,且每棵子树都满足。这个时候堆顶是本序列中最大的元素,因此将最后一个元素和堆顶元素调换,把最大值放到最终的位置上。建好了初始堆,就保留了排序时候的比较结果,后面的调整都可以再此基础上进行,加快排序效率。

  2、由于每次讲堆顶元素和最后一个对调,破坏了堆的性质,因此要从新向下调整,建立大顶堆(这里在初始堆的基础上,只要将堆顶元素调到合适位置即可)。

  3、调整完了之后,又将堆顶元素与未序区间的最后一个元素对调。

  4、重复2,3直到堆中剩余一个元素。

下面是对算法的代码:

1 void HeapSort(ElemType A[],int len)
2 {
3     BuilMaxHeap(A,len);//建立初始堆
4     for(int i = len;i>1;i--)
5     {
6         A[0]=A[1];A[1]=A[i];A[i]=A[0];//堆顶和未序区间尾元素对调。
7         AdjustDown(A,1,i-1);//调整未序区间,继续下一次对调。从1 —— i-1是未序的。
8     }
9 }

建立大顶堆的算法如下:

1 void BuildMaxHeap(ElemType A[],int len)
2 {
3     for(int i=len/2;i>0;i--)//从最后一个父节点开始向前建堆
4         AdjustDown(A,i,len);//向下调整
5 }

向下调整算法如下:

 1 void AdjustDown(Elemtype A[],int k,int len)
 2 {
 3     A[0]=A[k];//保存子堆的父节点
 4     for(int i=2*k;i<=len;i*=2)
 5     {
 6         if(i<len&&A[i]<A[i+1])//寻找较大的孩子
 7             i++;
 8         if(A[0]>A[i])//如果孩子节点没有比父节点大,跳出循环
 9             break;
10         else
11         {
12             A[k]=A[i];//孩子上调
13             k=i;//孩子当下一个父亲
14         }
15     }
16     A[k]=A[0];//把子堆顶元素放在最终的位置
17 }

注意:堆的最重要的操作就是调整函数,即向下调整AjustDown()月AdjustUp(),其余操作都建立在这两个操作之上。

删除堆顶元素时,需要堆顶元素和堆的最后一个元素对换,然后将堆的长度减一。

对堆查入操作时,先将堆的新节点放在堆的末尾,在对这个新节点指向向上调整AdjustUp()操作。

下面是向上调整的堆算法:

 

 1 void AdjustUp(Elentype A[],int k)//k为向上调整的节点,也为新堆的元素个数
 2 {
 3     A[0]=A[k];
 4     int i = k/2;//i是k的双亲
 5     while(i>0&&A[i]<A[0])//如果双亲存在,且双亲小于孩子节点,执行循环体中的语句
 6     {
 7         A[k]=A[i];
 8         k=i;
 9         i=k/2;//寻找双亲的双亲,继续向上比较
10     }//如果不满足while中的条件跳出,k的位置就是接下来新节点的位置。
11     A[k]=A[0];//将新节点复制到最终位置上
12 }

 

堆的应用:

堆可以用来解决TOPK问题;优先级队列的底层也是用堆来实现。

 

posted @ 2015-10-18 15:28 HOU_JUN 阅读(...) 评论(...) 编辑 收藏