调整堆以及堆排序

利用堆这种逻辑结构进行排序,难点主要在于创建堆与调整堆,利用堆进行排序无非就是大顶堆(左右孩子节点小于双亲节点)或者小顶堆(左右孩子节点都小于双亲节点)若节点下标从0开始,则每个节点的左孩子为2*i+1;右孩子节点为2*i+2;

1、定义堆结构

#define HEAPSIZE 20  //定义堆大小
#define INCSIZE 2     //定义的每次扩容的大小
typedef int ElemType;
 typedef struct
 {
     ElemType *data;
     int heapsize;  //堆的大小
     int cursize; //堆目前的元素个数
 }heap;

2、堆的基本操作(初始化堆,销毁堆,判空,判满,堆的元素个数)

 void init_heap(heap *hp)  //初始化堆
 {
     assert(hp!=NULL);
     hp->heapsize=HEAPSIZE;
     hp->cursize=0;
     hp->data=(ElemType *)malloc(sizeof(ElemType)*hp->heapsize);//heap->data指向堆空间
     if(hp->data==NULL)exit(1);
 }
 void destroy_heap(heap *hp) //销毁堆
 {
     assert(hp!=NULL);
     free(hp);
     hp->data=NULL;
     hp->heapsize=0;
     hp->cursize=0;
 }
 void clear(heap *hp)   //清空堆
 {
     assert(hp!=NULL);
     hp->cursize=0;
 }
 bool empty_heap(heap *hp)   //判空
 {
     assert(hp!=NULL);
     return hp->cursize==0;
 }
 bool full_heap(heap *hp)  //判满
 {
     assert(hp!=NULL);
     return hp->cursize==hp->heapsize;
 }
 int size_heap(heap *hp)  //求堆的元素个数
 {
     assert(hp!=NULL);
     return hp->cursize;
 }

3、调整堆
堆排序分为两步,一步是初始调整堆(即在第一次调整堆时,是从第一个非叶节点开始调整,直到调整到0节点,其中调整每个非叶节点时,是将其所包含的最大子树进行调整);另一步是在大顶堆(小顶堆)的基础上进行0节点与最后一个节点的元素交换,整个堆的元素减一,在由0节点开始向下调整最大子树,直到重新满足大顶堆(小顶堆)的要求;

void filter_down(ElemType *ar,int pos,int end,int flag)  //向下调整最堆函数
 {
     assert(ar!=NULL);
     int i=pos;int j=i*2+1;
     ElemType tmp=ar[i];
     while(j<=end)
     {
         if(flag==0)     //flag的作用是决定到底是选择大顶堆还是小顶堆进行调整,flag==0则是小顶堆
         {
              if( j+1<=end && ar[j]>ar[j+1])j+=1;
             if(ar[j]>=tmp)break;
         }
         else            //flag==1则是大顶堆
         {
             if(j+1<end && ar[j]<ar[j+1])j+=1;
             if(ar[j]<=tmp)break;
         }
         ar[i]=ar[j];
         i=j;
         j=i*2+1;
     }
     ar[i]=tmp;
 }
void make_heap(heap *hp,int flag)
{
    assert(hp!=NULL);
    int end=hp->cursize-1;
    int pos=(end-1)/2;  //从第一个非叶节点开始调整
    while(pos>=0)      
    {
        filter_down(hp->data,pos,end,flag);
        pos--;
    }
}
void insert_heap(heap *hp,ElemType *ar,int n,int flag)  //为了便于堆元素从数组ar中导入
{
    assert(hp!=NULL && ar!=NULL && n>0);
    for(int i=0;i<n;++i)
    {
        hp->data[i]=ar[i];
    }
    hp->cursize=n;
    make_heap(hp,flag);
}

void filter_up(heap *hp,int flag)//向上调整堆元素,对堆进行插入,需要插入到堆尾,则需要从插入位置向上调整堆
{
    int j=hp->cursize-1;
    int i=(j-1)/2;
    ElemType tmp=hp->data[j];
    while(j>0)
    {
        if(flag==0)   //同样flag为标志进行大顶堆(小顶堆)的调整
        {
            if(hp->data[i]<=tmp)break;
        }
        else
        {
            if(hp->data[i]>=tmp)break;
        }
        hp->data[j]=hp->data[i];
        j=i;
        i=(j-1)/2;
    }
    hp->data[j]=tmp;
}
void inc_heap(heap *hp)    //对堆进行扩容
{
    int n = INCSIZE+hp->cursize;
    hp->data=(ElemType*)realloc(hp->data ,sizeof(ElemType)*n);   //注意hp->data为堆上空间
    if(hp->data==NULL)exit(1);
    hp->heapsize=n;
}
 void push_heap(heap *hp,ElemType x,int flag)
 {
     assert(hp!=NULL);
     if(full_heap(hp))   //若果堆满了  则进行堆的扩容
     {
         inc_heap(hp);
     }
     hp->data[hp->cursize++]=x;
     filter_up(hp,flag);   //入堆元素是从堆尾入堆的 则需要从堆尾调整
 }

4、对堆进行排序

void print_heap(heap *hp)
{
    for(int i=0;i<hp->cursize;++i)
    {
        printf("%d ",hp->data[i]);
    }
    printf("\n");
}

void make_sort(heap *hp,int flag)
{
    make_heap(hp,flag);//第一步创建堆(从第一个非叶节点开始)
    int n=hp->cursize;
    for(int i=0;i<n-1;n--)
    {
        swap(&hp->data[0],&hp->data[n-1]); //依次将调整好的元素与堆进行分离(放到后面)
        filter_down(hp->data,0,n-2,flag);
    }
    print_heap(hp);
}

5、 有选择的进行大顶堆或者小顶堆时还可以用到宏定义


#define MINHEAP

  void filter_down(ElemType *ar,int start,int end)
{
    int i = start; // root;
    int j = i*2+1; // leftchild;
    ElemType tmp = ar[i];
    while(j <= end)
    {
#ifdef MINHEAP
        if(j < end && ar[j] >= ar[j+1]) j+=1;
        if(tmp <= ar[j])  break;
#else
        if(j < end && ar[j] <= ar[j+1]) j+=1;
        if(tmp >= ar[j]) break;
#endif
        ar[i] = ar[j];
        i = j;
        j = i*2+1;
    }
    ar[i] = tmp;
}  

其中堆排序还有一个应用最广的是找大数据的前几大或者前几小;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值