选择排序(三)--堆排序

一、堆排序思想 (要和shell排序区别)
   堆,是一棵完全二叉树,(完全二叉树概念:只有最后两层可以是业界点,而且最底层的叶节点都在左侧,图在上一篇日志上有)
   堆可以用数组来描述,在排序中强调的是 大根堆(最大值在树根),将大根堆的的根部值 和 无序数组的最后一个数交换,然后重新排序新的无序堆为 大根堆。


      具体排序思想:将待排序区分为无序区和有序区,无序区在前,有序区在后。未排序前无序区为整个待排序区间,没有有序区。
     排序的过程是,不断地将无序区构造成一个大根堆,这样无序区中的最大元素便被放置在了数组的最前面即根的位置,然后将无序区的第一个元素与最后一个元素交换,此动作将无序区的长度缩小一,将有序区的长度扩大一,即有序区前进一,无序区向前退一。然后将新的无序区(由于根节点的变化,此时很可能已经不是大根堆)重新调整为大根堆,等待下一次的交换。如此往复,不断地将无序区的最大元素添加到有序区的前面,同时缩小无序区,直到有序区占满待排序区为止。

这样,还剩下两个问题: 1.如何将一个交换后的无序区调整为大根堆; 2.如何在排序之前建立那个初始的大根堆。而第二个问题是可以通过第一个问题的解决而解决的。
      1.如何将一个交换后的无序区调整为大根堆?
      由于进行元素交换前,无序区是一个大根堆,即左子树和右子树都是大根堆,所以根节点变化后左右子树仍然都是大根堆,无序区里的最大元素一定在新根节点、左右子树的根节点这三个结点里。先存储新的根节点的值以待后用。如果新的根结点最大,说明已经是大根堆,调整完毕; 
      否则比较左右子树根节点,找出较大的,它是无序区现存的最大元素,应该作为新的大根堆中的根,所以将此节点上调至根节点位置,接下来就只需要调整此结点原来所在的子树为大根堆即可(因为大根堆对左右子树之间元素的大小关系没有要求!)。
       2..那么如何利用此过程建立初始大根堆呢?
       若数组下标范围为0~n,考虑到单独一个元素是大根堆,则从下标[n/2]开始的元素均为大根堆(叶节点)于是只要从[n/2]-1开始,向前依次构造大根堆,这样就能保证,构造到某个节点时,它的左右子树都已经是大根堆(这就可以调用我们上面讨论的调整为大根堆的方法了)。一直到下标为0的结点,就完成了初始大根堆的建立。 堆排序用到的函数有两个:1个将左右子树都为大根堆的完全二叉树调整为大根堆的调整函数;1个反复调用此调整函数来进行排序的排序函数。

/*选择排序 3) 堆排序*/ template<class T> void HeapAdjust(SqList<T> &L,int s, int m) { //对顺序表做查找,从最大的孩子结点向下筛选,找到最大值 T rc=L.key[s]; for(int j=2*s;j<=m;j*=2) { if(j<m&&L.key[j]<=L.key[j+1]) //j=2*s为左子结点,j+1为右结点 j++; if(rc>L.key[j]) //即当前父亲结点 比 左右结点都大,已经是大根堆 break; L.key[s]=L.key[j]; s=j; //继续往子树的底层移动,将该子树构造为大根堆 } L.key[s]=rc; } template<class T> void HeapSort(SqList<T> &L) { T value; int i; cout<<"把L.key[1...L.length]调整为大顶堆--/n"; for(i=L.length/2;i>0;i--) { HeapAdjust(L,i,L.length); Output(L); } cout<<"初始化完大根堆/n迭代排序:/n"; for(i=L.length;i>1;i--) { value=L.key[1]; /*将大根堆的 根部 和最后一个无序区数据 交换 */ L.key[1]=L.key[i]; L.key[i]=value; HeapAdjust(L,1,i-1); Output(L); } }  

输入:50,435,6437,768,8,45,567,1435;
从第length/2开始构造,即第4个768开始构造大根堆

选择排序(三)--堆排序_第1张图片

你可能感兴趣的:(shell,存储,output)