zoukankan      html  css  js  c++  java
  • 数据结构-堆 接口定义与实现分析(详细注释与图解)

    如果想了解堆的概念,可以点击此处查看前面关于堆的定义的随笔。

    堆的操作接口包括初始化堆、销毁堆、向堆中插入元素、从堆顶移除元素、堆的结点个数。

    我们用heap来命名一个堆。下面是对以上接口的定义:

    heap_init


     void heap_init(Heap *heap,int (*compare)(const void key1,const void key2),void (*destroy)(void *data));

    返回值:无

    描述初始化堆heap

    在对堆执行其他操作之前,必须要对其进行初始化。

    函数compare用来比较堆中的结点大小,如果堆为最大值堆,当key1>key2时,函数返回1;当key1=key2时,函数返回0;当key1<key2时,函数返回-1。在最小值堆中返回值正好相反。

    函数指针destroy,通过调用heap_deatroy来释放动态分配的内存空间。如果堆中的数据不需要释放,那么destroy应该指向NULL。

    复杂度:O(1)

    heap_destroy


     void heap_destroy(Heap *heap);

    返回值:无

    描述销毁堆heap

    在调用heap_destroy之后不再允许进行其他操作。

    heap_destroy会删除堆中的所有结点,在删除的同时调用heap_init中的destroy所指向的销毁函数(此函数指针不为NULL)。

    复杂度:O(n),n代表堆中的结点个数。

    heap_insert


     int heap_insert(Heap *heap,const void *data);

    返回值:如果插入元素成功则返回0,否则返回-1。

    描述向堆heap中插入一个结点

    新结点包含一个指向data的指针,只要结点仍然存在于堆中,此指针就一直有效。与data有关的内存空间将由函数的调用者来管理。

    复杂度:O(lg n),n代表堆中的结点个数。

    heap_extract


     int heap_extract(Heap *heap,void **data);

    返回值:如果结点释放成功则返回0,否则返回-1。

    描述从堆heap中释放堆顶部的结点

    返回时,data指向被释放结点中存储的数据。与data相关的内存空间将由函数的调用者来管理。

    复杂度:O(lg n),n代表堆中的结点个数。

    heap_size


     int heap_size(const Heap *heap);

    返回值:堆中结点的个数。

    描述:这是一个获取堆heap中结点个数的宏。

    复杂度:O(1)。

    下面来分析一下如何实现堆:

    我们用来叉树来实现堆,将其结点按照树的层次结构存放在一个数组中。我们令Heap作为堆的数据结构,此结构体包含4个成员:

    1、size指明堆中的结点个数;2、compare与3、destroy是用于封闭传入heap_init的函数的指针;4、tree是堆中存储结点的数组。

    堆的头文件示例如下:

    /*heap.h*  堆的头文件/
    #ifndef HEAP_H
    #define HEAP_H
    
    /*定义堆的数据结构体*/
    typedef struct Heap_
    {
        int size;
        
        int (*compare)(const void *key1,const void *key2);
        void (*destroy)(void *data);
        
        void **tree;
    }Heap;
    
    /*公共接口部分*/
    void heap_init(Heap *heap,int (*compare)(const void *key1,const void key2),void (*destroy)(void *data));
    void heap_destroy(Heap *heap);
    int heap_insert(Heap *heap,const void *data);
    int heap_extract(Heap *heap,void **data);
    
    #define heap_size(heap)((heap)->size)
    
    #endif // HEAP_H

    堆的实现示例如下:

    /*heap.c*/
    #include <stdlib.h>
    #include <string.h>
    
    #include "heap.h"
    /*定义heap执行中需要使用的私有宏*/
    #define heap_parent(npos) ((int)(((npos)-1)/2))  /*npos的父结点*/
    #define heap_left(npos) (((npos)*2)+1)           /*npos的左兄弟结点*/
    #define heap_right(npos) (((npos)*2)+2)          /*npos的右兄弟结点*/
    
    /*heap_init 堆的初始化*/
    void heap_init(Heap *heap,int (*compare)(void *key1,void *key2),void (*destroy)(void *data))
    {
        /*只需要将size设为0,destroy成员指向destroy,将tree指针设置为NULL*/
        heap->size = 0;
        heap->compare = compare;
        heap->destroy = destroy;
        heap->tree = NULL;
    
        return ;
    }
    /*heap_destroy  销毁堆*/
    void heap_destroy(Heap *heap)
    {
        int i;
        /*移除堆中所有的结点*/
        if(heap->destroy != NULL)
        {
            for(i=0; i<heap_size(heap);i++)
            {
                /*调用用户自定义函数释放动态分配的数据*/
                heap->destroy(heap->tree[i]);
            }
        }
        /*释放为堆分配的空间*/
        free(heap->tree);
    
        memset(heap,0,sizeof(Heap));
        return;
    }
    
    /*heap_insert  向堆中插入结点*/
    int heap_insert(Heap *heap,const void *data)
    {
        void *temp;
        int  ipos;
             ppos;
    
        /*为结点分配空间*/
        if((temp = (void **)realloc(heap->tree,(heap->size(heap)+1)*sizeof(void *))) == NULL)
        {
            return -1;
        }
        else
        {
            heap->tree = temp;
        }
        /*将结点插入到堆的最末端*/
        heap->tree[heap_size(heap)] = (void *)data;
        /*将新结点向上推动,恢复堆的排序特点*/
        ipos = heap_size(heap);    /*堆结点数的数值*/
        ppos = heap_parent(ipos);  /*ipos位置结点的父结点*/
    
        /*如果堆不为空,并且末位结点大于其父结点,则将两个结点进行交换*/
        while(ipos>0 && heap->compare(heap->tree[ppos],heap->tree[ipos])<0)
        {
            /*交换末端结点与其父结点的位置*/
            temp = heap->tree[ppos];
            heap->tree[ppos] = heap->tree[ipos];
            heap->tree[ipos] = temp;
    
            /*将定位结点向上移动一层,以继续执行堆排序*/
            ipos = ppos;
            ppos = heap_parent(ipos);
        }
    
        /*堆插入与排序完成,调整堆的结点数量值*/
        heap->size++;
    
        return 0;
    }
    
    /*heap_extract 释放堆顶部的结点*/
    int heap_extract(Heap *heap,void **data)
    {
        void *save,
             *temp;
        int  ipos,lpos,rpos,mpos;
    
        /*不允许从空的堆中释放结点*/
        if(heap->size(heap) == 0)
            return -1;
    
        /*释放堆顶部的结点*/
        /*首先将data指向将要释放结点的数据*/
        *data = heap->tree[0]
    
        /*将save指向未位结点*/
        save = heap->tree[heap_size(heap)-1];
    
        if(heap_size(heap)-1 > 0)
        {   /*为堆分配一个稍小一点的空间*/
            if((temp = (void **)realloc(heap->tree,(heap_size(heap)-1)*sizeof(void *)))==NULL)
            {
                return -1;
            }
            else
            {
                heap->tree = temp;
            }
            /*调整堆的大小*/
            heap->size--;
        }
        else
        {   /*只有一个结点,释放并重新管理堆,并返回*/
            free(heap->tree);
            heap->tree = NULL;
            heap->size = 0;
            return 0;
        }
        /*将末位结点拷贝到根结点中*/
        heap->tree[0] = save;
    
        /*重新调整树的结构*/
        ipos = 0;                /*顶元素*/
        lpos = heap_left(ipos);  /*左子结点*/
        rpos = heap_right(ipos); /*右子结点*/
    
        /*父结点与两个子结点比较、交换,直到不再需要交换为止,或者结点到达一个叶子位置*/
        while(1)
        {
            /*选择子结点与当前结点进行交换*/
            lpos = heap_left(ipos);
            rpos = heap_right(ipos);
            /*父结点与左子结点位置不正确,左子结点大于其父结点*/
            if(lpos < heap_size(heap) && heap->compare(heap->tree[lpos],heap->tree[ipos])>0)
            {
                mpos = lpos;  /*将左子结点的位置赋给mpos(最大位置)*/
            }
            else
            {
                mpos = ipos;
            }
    
            if(rpos < heap_size(heap) && heap->compare(heap->tree[rpos],heap->tree[mpos])>0)
            {
                mpos = rpos;
            }
    
            /*当mpos和ipos相等时,堆特性已经被修复,结束循环*/
            if(mpos == ipos)
            {
                break;
            }
            else
            {
                /*交换当前结点与被选中的结点的内容*/
                temp = heap->tree[mpos];
                heap->tree[mpos] = heap->tree[ipos];
                heap->tree[ipos] = temp;
    
                /*下移一层,以继续执行堆排序*/
                ipos = mpos;
            }
        }
        return 0;
    }

     下图是heap_insert,向最大值堆中插入结点的过程图解示例

    下图是将一个元素从最大值堆中释放的过程图解示例:

  • 相关阅读:
    三国杀三字经
    window.open参数表
    捕获FormRequest安全错误的类
    SqlServer之Convert 函数应用格式化日期
    数据库区分大小写的问题
    时间字符串的比较
    char码值对应列表大全
    web.config 连接字符串 加密解密,代码方式
    关于app_offline.htm的用法
    关闭窗口的时候弹出确认对话框
  • 原文地址:https://www.cnblogs.com/idreamo/p/8564426.html
Copyright © 2011-2022 走看看