几大内部排序算法(三)--堆(优先队列)和堆排序

这儿是我的笔记,希望大家可以友好交流!!谢谢#__#

这是最后一篇关于排序的了,在说堆排序前想先说下优先队列的ADT实现,优先队列最常见的用标准二叉堆实现,标准二叉堆其实就是用完全文二叉树实现的,由于完全二叉树的特点,仅仅用数组就可以实现(其实是将数抽象了,数组化了)。

(一)优先队列–标准二叉堆ADT实现
1:堆的结构性:当用标记法时(0位置用最小或最大的值填充)对数组中任意i上的元素,其左儿子在2*i上,右儿子在2*i+1上,其父亲在i/2;
当不用标记法时,对数组中任意i上的元素,其左儿子在2*i+1上,右儿子在2*i+1+1上,其父亲在(i-1)/2;
:2:堆的有序性:想要快速的找到最小的(大)的元,最小(大)的元应该在根节点上,数组的0或1位置上(看是不是用了标记法),此外,每个带有儿子的节点,其值要比孩子大或者小。

3:基本操作:
Insert:为将某元素x插入,应该在最后面创建一个空穴,若x可以直接放入而不破坏堆的有序性,则直接插入,否则将父亲移到空穴位置,空穴上移动了一层往根方向(上滤)。
【注意,在后面堆排序建堆时,也是从后面的非叶子节点开始检验调整的】

DeleteMax/Min:相当于出列,删除最小或最大的元素,把最小的元素从根处删除,根处多了一个空穴,则需要将最后一个元素相应的位置。若此时x放到空穴上,比根节点两个儿子中较大的元素大,则x放到空穴后完成;若x不能放,则选择2个儿子较大的放到空穴,空穴下移动(下滤),直到x能当道合适的位置。

4:其他操作:
DecreseKey(P,deta,H)
给在某个位置p处的元素降低deta,再通过调整达到平衡。
IncreseKey(P,deta,H)
给在某个位置p处的元素增加deta,再通过调整达到平衡。

以下是没有用标记法的大根堆ADT。
头文件

#ifndef _BIAOZHUNERCHADUI_H_
#define _BIAOZHUNERCHADUI_H_
typedef (*OPERATOR)();
typedef enum
{
    false = 0,
    true,
}bool;
struct Heap;
typedef struct Heap HEAP;
typedef struct Heap* HEAP_T;

HEAP_T Heap_Init(HEAP_T HP,int N);
//为了build——heap
bool Heap_Insert(HEAP_T HP,OPERATOR cmp,void* data);
void*  Heap_DeleteMax(HEAP_T HP,OPERATOR cmp,OPERATOR myfree,void* sxw);
void  Heap_DeleteAll(HEAP_T HP,OPERATOR op,void* sxw);
int Heap_GetSize(HEAP_T HP);
int Heap_GetLen(HEAP_T HP);
void Heap_Printf(HEAP_T HP);
#endif

实现:

#include"biaozhunerchadui.h"
#include<stdlib.h>
/**因为有0*/
//#define leftchild(i) (2*i+1)
//#define father(child) (child-1)/2
struct Heap
{
    void** data;
    int size;
    int len;
};
HEAP_T Heap_Init(HEAP_T HP,int N)
{
    if(N<0)
    {
        printf("堆的大小不合适而exit(0)\n");
        exit(0);
    }
    HP = (HEAP_T)malloc(sizeof(struct Heap));
    if(HP==NULL)
        return NULL;
    HP->size = N;
    HP->len = -1;
    HP->data = (void**)malloc(HP->size * sizeof(void*));
    if(HP->data == NULL)
    {
        free(HP);
        HP = NULL;
        return NULL;
    }
    for(int i = 0; i < HP->size; i++)
    {
        HP->data[i] = NULL;
    }
    return HP;
}
bool Heap_Insert(HEAP_T HP,OPERATOR cmp,void* data)//为了build——heap
{
    if(data == NULL || HP == NULL)
    {
         printf("插入失败,数据为空或堆为空\n");
          return false;
    }
    //前面HP->len初始化为-1,所以为了能用上0,先加上去
    int index = HP->len+1;
    if(HP->data[index] == NULL)
    {
        HP->data[index] = (void*)malloc(sizeof(void*));
        if(HP->data[index]  == NULL)
        {
            printf("插入时分配空间失败\n");
            return false;
        }
    }
    while(index!=0)
    {
        if(cmp!=NULL)
        {
            if(cmp(data,HP->data[index/2]))
            {
                HP->data[index] = HP->data[index/2];
                HP->data[index/2] = data;
                index = index/2;
            }
           else
            {
                HP->data[index] = data;
                HP->len++;
                return true;
            }
        }
        else
        {
            if(*(int*)data > *(int*)HP->data[index/2])
            {
                HP->data[index] = HP->data[index/2];
                HP->data[index/2] = data;
                index = index/2;
            }
            else
            {
                HP->data[index] = data;
                HP->len++;
                return true;
            }
        }
    }

    HP->data[index] = data;
    printf("元素%d入堆了\n",*(int*)HP->data[index]);
    HP->len++;
    return true;

}
void*  Heap_DeleteMax(HEAP_T HP,OPERATOR cmp,OPERATOR myfree,void* sxw)
{
    void* res = HP->data[0];
    int child = 0, i = 0;
    if(HP == NULL)
    {
        printf("堆是空的,初始化\n");
        return NULL;
    }
    if(HP->len < 0)
    {
        printf("队列元素已经空了\n");
        return NULL;
    }
    child = 1;
    //删除完要从根部开始调整,while内说明至少有儿子
    while(child <= HP->len)
    {
        if(cmp!=NULL && myfree!=NULL)
        {
            if(cmp(HP->data[child+1],HP->data[child]))
                child++;
            if(cmp(HP->data[HP->len],HP->data[child]))
            {
                HP->data[child/2] = HP->data[HP->len];
            /**不释放那个元素的单位,留到最后deleteall一起做处理,但是如果不释放,再入列的话,就会去比较了
            所以还是要进行释放然后置NULL的
            */
                if(myfree!=NULL)
                {
                    myfree(HP->data[HP->len]);
                }
                HP->data[HP->len] = NULL;
                break;
            }
            else
            {
                HP->data[child/2] = HP->data[child];
                child = child *2;
            }
        }
        else
        {
            /**两个儿子时找最大的*/
            if(HP->data[child+1]&&HP->data[child])
            {
                if(*(int*)HP->data[child+1] > *(int*)HP->data[child])
                    child++;
                if(*((int*)(HP->data[HP->len])) > *((int*)(HP->data[child])))
                {
                    HP->data[(child-1)/2] = HP->data[HP->len];
                    HP->data[HP->len] = NULL;
                    HP->len--;
                    return res;
                }
                else
                {
                    HP->data[(child-1)/2] = HP->data[child];
                    child = child*2+1;
                }
            }
            /**只有一个孩子说明到底了*/
            else if(HP->data[child+1] == NULL || HP->data[child] == NULL)
            {
                HP->data[(child-1)/2] = HP->data[HP->len];
                HP->data[HP->len] = NULL;
                HP->len--;
                return res;
            }

        }

    }
    //没有孩子后决定最后一个元素是否要填补
    if((child-1) / 2 < HP->len )
    {
        HP->data[(child)/2] = HP->data[HP->len];
        if(myfree!=NULL)
            myfree(HP->data[HP->len]);

    }
    HP->data[HP->len] = NULL;
    HP->len--;
    return res;
}
void  Heap_DeleteAll(HEAP_T HP,OPERATOR op,void* sxw)
{
    if(HP==NULL)
    {
        printf("堆是无效的\n");
        exit(0);
    }
    int index = Heap_GetLen(HP);
     if(op!=NULL)
    {
        for(;index>=0;index--)
        {
            op(HP->data[index],sxw);
            HP->data[index] = NULL;
        }
    }
    free(HP);
    HP = NULL;

}
int Heap_GetSize(HEAP_T HP)
{
    if(HP == NULL) exit(0);
    else
        return HP->size;
}
int Heap_GetLen(HEAP_T HP)
{
    if(HP == NULL) exit(0);
    else
        return HP->len;
}
//用于检验的
void Heap_Printf(HEAP_T HP)
{
    int i = 0;
    for(i = 0;i <= Heap_GetLen(HP);i++)
    {
        printf("%d\t",*(int*)HP->data[i]);
    }
    printf("\n");
}

关于小堆的也很简单,只要理解了deletamax后,其他的increaseKe就很简单。

(二)堆排序
堆排序,其实就是借助于每次从根处得到最大的(小)的值,连续N次就可以排好了,为了不用临时数组,所以不借助ADT的实现,直接从无序的数组(无序堆序列)开始先建一个有序堆,然后开始排序。
第一步:把无序堆建成有序堆
从后面的非叶子节点开始(N/2,可以画出而得到)调整,使得有子节点的根大(或者小)
for(i= N/2;i>=0;i–)调整这么多非叶子结点

第二步:多次排序调整
交换第1各元素和最后一个元素,并减小的对的大小,此时根处的有序性被破坏,所以从根处调整。

实现:
一个很关键的函数,从堆A的某个位置index调整

void PercDown(int *A ,int index,int N)
{

    int tmp = A[index];
    int child = 2*index+1;
    while(child < N)//至少有一个子节点
        {
            if(child< N && child+1 < N)//2个节点时
            {
                if(A[child+1] > A[child])
                    child++;
                if(tmp < A[child])
                {
                    A[(child-1)/2] = A[child];
                    A[child] = tmp;
                    child = 2*child + 1;
                }
                else
                    break;
            }
            else if(child < N && child+1 >=N)//一个节点
            {
                if(tmp < A[child])
                {
                    A[(child-1)/2] = A[child];
                    A[child] = tmp;
                    break;
                }
                else
                    break;

            }
            else//无节点
                break;

        } 
  //为了和之前的堆方法搭配所以就多一步可以直接A[(child-1)/2]=tmp;
        if((child-1)/2 < N)
        {
            A[(child-1)/2] = tmp;
        }
}

建堆

void Heap_Bulid(int* A,int N)
{
    int index = N/2;
    for(;index >= 0 ; index--)  
    /**从N/2处开始更新每个非叶子节点*/
    {
        PercDown(A,index,N);
    }
}

排序

void Heap_Sort(int *A,int N)
{
    Heap_Bulid( A, N);
    /**实际排序操作*/
    for(int i=N;i>0;i--)
    {
        Swap(A,0,i-1);
        //从根处调整
        PercDown(A,0,i-1);
    }
}

测试代码可以用“排序算法(一)中的测试代码”。
用数组实现的优先队列不能支持合并操作,所以还有后面会和树,散列表那些一起补记回来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值