热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构堆接口定义与实现分析(详细注释与图解)

如果想了解堆的概念,可以点击此处查看前面关于堆的定义的随笔。堆的操作接口包括初始化堆、销毁堆、向堆中插入元素、从堆顶移除元素、堆的结点个数。我们用heap来命名一个堆。下面是对以上

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

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

我们用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

函数指针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 
#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->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 compare(heap->tree[lpos],heap->tree[ipos])>0)
        {
            mpos = lpos;  /*将左子结点的位置赋给mpos(最大位置)*/
        }
        else
        {
            mpos = ipos;
        }

        if(rpos 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,向最大值堆中插入结点的过程图解示例

技术分享图片

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

技术分享图片

数据结构-堆 接口定义与实现分析(详细注释与图解)


推荐阅读
  • malloc 是 C 语言中的一个标准库函数,全称为 memory allocation,即动态内存分配。它用于在程序运行时申请一块指定大小的连续内存区域,并返回该区域的起始地址。当无法预先确定内存的具体位置时,可以通过 malloc 动态分配内存。 ... [详细]
  • 本文介绍了多种开源数据库及其核心数据结构和算法,包括MySQL的B+树、MVCC和WAL,MongoDB的tokuDB和cola,boltDB的追加仅树和mmap,levelDB的LSM树,以及内存缓存中的一致性哈希。 ... [详细]
  • Python多线程详解与示例
    本文介绍了Python中的多线程编程,包括僵尸进程和孤儿进程的概念,并提供了具体的代码示例。同时,详细解释了0号进程和1号进程在系统中的作用。 ... [详细]
  • 本文详细介绍了Linux系统中用于管理IPC(Inter-Process Communication)资源的两个重要命令:ipcs和ipcrm。通过这些命令,用户可以查看和删除系统中的消息队列、共享内存和信号量。 ... [详细]
  • NX二次开发:UFUN点收集器UF_UI_select_point_collection详解
    本文介绍了如何在NX中使用UFUN库进行点收集器的二次开发,包括必要的头文件包含、初始化和选择点集合的具体实现。 ... [详细]
  • 本文介绍了如何在 ASP.NET 中设置 Excel 单元格格式为文本,获取多个单元格区域并作为表头,以及进行单元格合并、赋值、格式设置等操作。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • [c++基础]STL
    cppfig15_10.cppincludeincludeusingnamespacestd;templatevoidprintVector(constvector&integer ... [详细]
  • 本文详细介绍了如何解决DNS服务器配置转发无法解析的问题,包括编辑主配置文件和重启域名服务的具体步骤。 ... [详细]
  • importpymysql#一、直接连接mysql数据库'''coonpymysql.connect(host'192.168.*.*',u ... [详细]
  • 微软推出Windows Terminal Preview v0.10
    微软近期发布了Windows Terminal Preview v0.10,用户可以在微软商店或GitHub上获取这一更新。该版本在2月份发布的v0.9基础上,新增了鼠标输入和复制Pane等功能。 ... [详细]
  • 自动验证时页面显示问题的解决方法
    在使用自动验证功能时,页面未能正确显示错误信息。通过使用 `dump($info->getError())` 可以帮助诊断和解决问题。 ... [详细]
  • 数字资产量化交易通过大数据分析,以客观的方式制定交易决策,有效减少人为的主观判断和情绪影响。本文介绍了几种常见的数字资产量化交易策略,包括搬砖套利和趋势交易,并探讨了量化交易软件的开发前景。 ... [详细]
  • 自定义滚动条美化页面内容
    当页面内容超出显示范围时,为了提升用户体验和页面美观,通常会添加滚动条。如果默认的浏览器滚动条无法满足设计需求,我们可以自定义一个符合要求的滚动条。本文将详细介绍自定义滚动条的实现过程。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
author-avatar
四海承风2502893247
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有