堆排序

自己写的程序实现:

值得注意的是:HeapAdust这个函数里面s为最中间的时候,他只有一个子节点的情况 那么 j+1造成数组越界,

因此一定要限制 j<m的情况下比较j和 j+1 也就是如果j=m 则 j就是本身了 不用跟j+1 比较


第二点: 数组的下标从零开始的,因此HeapSort ,最中间的那个s = n/2-1  子节点为 2*s+1 h 2*s + 2,而且最后一个节点不用再比较了


#include <iostream>
#include <time.h>
#include <string.h>
#include <stdlib.h>



using namespace std;
void HeapSort ( int *a,int n);
void HeapAdjust( int *a,int s, int m);
void printArray(int (&a)[10])
{
    for(int i =0; i<10; i++)
    {
        cout<< a[i]<<" ";
    }
}

void swap( int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    while(true)
    {
        system("cls");
        time_t t;
        srand( (unsigned) time(&t));
        int a[10] = {0};
        for(int i = 0;i<10;i++)
        {
            a[i] = rand() % 11;
        }
        printArray(a);

        HeapSort(a,10);
        cout<<endl;
        printArray(a);
        cin.get();

    }

    return 0;
}

void HeapAdjust( int *a,int s, int m)
{
    for(int j=s * 2+1; j<=m; j = j*2+1)
    {
        //迭代的把当时这个元素赋值给一个临时的变量
        //如果比子节点那个大的还小就交换两个值
        //然后把新的那个值当成a[s]再循环
        int temp = a[s];
        //当
        if(j<m&&(a[j]<a[j+1])) j++;

        if(temp > a[j]) break;

        if(temp < a[j])
        {
            a[s] = a[j];
            s=j;
            a[j] = temp;

        }

    }

}

void HeapSort ( int *a, int n)
{
    for(int i = n/2 -1;i>=0;i--)
    {
        HeapAdjust(a,i,n-1);
    }
    for(int j = n-1;j>0;j--)
    {
       swap(a[0],a[j]);
     //当只有两个节点情况下,直接交换就好了,不需要再调整,否则浪费时间,直接跳出循环
       if(j==1) break;
       //把第一个预案算a[0]每次最后一个a[j]交换以后,最后一个就不用比较了,因此j-1
       HeapAdjust(a,0,j-1);
    }

}



最近面试,老是被问到堆排序算法。
回答时老是感觉思路不清楚,现在总结一下,把思路弄清楚的。

1.堆排序是利用堆的特性对记录序列进行排序的一种排序方法。
好的那么堆得特性是什么呢?
堆得定义:
堆是满足下列性质的数列{r1, r2, …,rn}:

 
如下图最开始是一个小顶堆。当把97和13 交换后不是堆了,所以我们要调整根节点使之成为堆即筛选。(注意:是自 堆顶到叶子的筛选过程,应该刚开始是堆由于把堆顶给换了,罪魁祸首是堆顶,其它小范围还是堆,所以是从堆顶开始)。

这其中还要注意一点。97 与13 交换后应该跟27 比较为什么呢?
1.因为是小顶堆,所以在97 的子节点里选择小者。如果把38放上去。38成了27的父节点比27大就不是小顶堆了。如果换成大顶堆就要比较把大的数据放上去。
所以程序里交换时要先要比较一下。
程序如下:
  1. //堆调整算法   
  2. void HeapAdjust (HeapType &H, int s, int m)  
  3. {   // 已知 H.r[s..m]中记录的关键字除 H.r[s] 之外   
  4.     //均满足堆的特征,本函数自上而下调整 H.r[s]   
  5.     //的关键字,使 H.r[s..m] 也成为一个大顶堆   
  6.      rc = H.r[s];    // 暂存 H.r[s]    
  7.      for ( j=2*s; j<=m; j*=2 ) { // j 初值指向左孩子   
  8.     自上而下的筛选过程;  
  9.      }  
  10.      // 自上而下的筛选过程   
  11.       if ( j<m && H.r[j].key>H.r[j+1].key )  ++j;       
  12.              // 左/右“子树根”之间先进行相互比较   
  13.              // 令 j 指示关键字较小记录的位置   
  14.       if ( rc.key <= H.r[j].key )  break;   
  15.            // 再作“根”和“子树根”之间的比较,   
  16.            // 若“>=”成立,则说明已找到 rc 的插   
  17.            // 入位置 s ,不需要继续往下调整   
  18.   
  19.       H.r[s] = H.r[j];   s = j;      
  20.         // 否则记录上移,尚需继续往下调整   
  21.   
  22.     H.r[s] = rc;  // 将调整前的堆顶记录插入到 s  (注意插入的位置为s j=2*s)   
  23. // HeapAdjust  
//堆调整算法
void HeapAdjust (HeapType &H, int s, int m)
{   // 已知 H.r[s..m]中记录的关键字除 H.r[s] 之外
    //均满足堆的特征,本函数自上而下调整 H.r[s]
    //的关键字,使 H.r[s..m] 也成为一个大顶堆
     rc = H.r[s];    // 暂存 H.r[s] 
     for ( j=2*s; j<=m; j*=2 ) { // j 初值指向左孩子
    自上而下的筛选过程;
     }
     // 自上而下的筛选过程
      if ( j<m && H.r[j].key>H.r[j+1].key )  ++j;     
             // 左/右“子树根”之间先进行相互比较
             // 令 j 指示关键字较小记录的位置
      if ( rc.key <= H.r[j].key )  break; 
           // 再作“根”和“子树根”之间的比较,
           // 若“>=”成立,则说明已找到 rc 的插
           // 入位置 s ,不需要继续往下调整

      H.r[s] = H.r[j];   s = j;    
        // 否则记录上移,尚需继续往下调整

    H.r[s] = rc;  // 将调整前的堆顶记录插入到 s  (注意插入的位置为s j=2*s)
} // HeapAdjust

2)建堆是一个从下往上进行“筛选”的过程 (首先要把底部的建成小堆,前面调整是因为只有堆顶,其它都已经是堆了。当我建堆到堆顶是也是从堆顶往下筛选)( 所以说建堆大范围是从下往上筛选,在添加该结点时,还得从该节点往下筛选确保添加该节点后还是堆 )。
如下图建堆过程:  从97 开始->65->38 ->49这是从下往上(大范围从下往上)。第二个图到65时又 65与13 调整了(从上往下调整)。当到49时也是49<-> 13  <-> 27所以也是从上之下调整( 为了确保加入该结点后还是堆)。



程序如下:
堆排序算法如下:
  1. void HeapSort ( HeapType &H ) {  
  2.   // 对顺序表 H 进行堆排序   
  3. for ( i=H.length/2;   i>0;   --i )  
  4.      HeapAdjust ( H.r, i, H.length );    // 建小顶堆   
  5.   
  6. for ( i=H.length; i>1; --i ) {  
  7.      H.r[1]←→H.r[i];             
  8.           // 将堆顶记录和当前未经排序子序列   
  9.           //  H.r[1..i]中最后一个记录相互交换   
  10.      HeapAdjust(H.r, 1, i-1);  // 对 H.r[1] 进行筛选   
  11. }  
  12. // HeapSort  
void HeapSort ( HeapType &H ) {
  // 对顺序表 H 进行堆排序
for ( i=H.length/2;   i>0;   --i )
     HeapAdjust ( H.r, i, H.length );    // 建小顶堆

for ( i=H.length; i>1; --i ) {
     H.r[1]←→H.r[i];           
          // 将堆顶记录和当前未经排序子序列
          //  H.r[1..i]中最后一个记录相互交换
     HeapAdjust(H.r, 1, i-1);  // 对 H.r[1] 进行筛选
}
} // HeapSort

note: 堆排序算法以前看过几遍老是忘,问得时候思路不太清晰。只要把关键几个点弄清楚,把思路搞清楚了以后就不怕了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值