目录

1.堆的概念结构及分类

1.2堆的分类

1.2.1 大堆

1.2.2 小堆

2. 堆的主要接口

3.堆的实现

3.1 堆的初始化 HeapInit

3.2 堆的销毁 HeapDestory

3.3 堆的打印 HeapPrint

3.4 堆的插入元素 HeapPush   *

功能分析:

功能实现:

3.5 堆的删除元素 HeapPop  *

功能分析:

功能实现:

3.6 判断是否为空 HeapEmpty

3.7 求元素个数

3.8 求堆顶元素

4.堆的应用:堆排序   ***

4.1 堆排序实现过程分析

4.2 堆排序实现代码

4.3 堆排序结果演示

5.堆(小堆)的完整代码


1.堆的概念结构及分类

以上这段概念描述看起来十分复杂,晦涩难懂。那么堆用通俗语言简单描述如下:

堆是一个完全二叉树的顺序存储。在一个堆中,堆的父节点一定大于等于(或小于等于)子节点。一旦有一部分不满足则不为堆。

堆的性质: 

1、堆中某个节点的值总是不大于或不小于其父节点的值;
2、堆总是一棵完全二叉树

1.2堆的分类

1.2.1 大堆

在一个堆中,父节点一定大于等于子节点的堆称为大堆。又称大根堆。

1.2.2 小堆

在一个堆中,父节点一定小于等于子节点的堆称为小堆。又称小根堆。(下图就是一个小堆)

习题练习:

1.下列关键字序列为堆的是:(A)
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

分析:选项A分析后为大堆,其他选项多多少少都存在错误。(画图分析如下)

2. 堆的主要接口

在本篇文章中我们主要以小堆为例实现。

现实中我们通常把堆使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

其中堆中包括以下主要功能:

1.堆的初始化   2.堆销毁  3.堆打印  4.堆的插入元素  5.堆删除元素   6.判断堆是否为空  7.求堆中元素的个数  8.求堆顶元素

详细接口如下:

//小堆
//算法逻辑思想是二叉树,物理上操作的是数组中数据
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;   //数组asize_t size;     //下标size_t capacity; //容量
}HP;void Swap(HPDataType* pa, HPDataType* pb);//交换函数
void HeapInit(HP* php);//堆初始化
void HeapDestory(HP* php);//堆销毁
void HeapPrint(HP* php);//堆打印//插入x以后,仍然要保证堆是(大/小)堆
void HeapPush(HP* php, HPDataType x);//删除堆顶的数据(最大/最小)
void HeapPop(HP* php);bool HeapEmpty(HP* php); //判断是否为空
size_t HeapSize(HP* php);//求元素个数
HPDataType HeapTop(HP* php);//求堆顶元素

3.堆的实现

有了如上的接口,接下来我们实现各个接口。由于我们使用数组来实现堆,大多接口功能和顺序表的实现相同。相同的实现这里不再过多分析。

3.1 堆的初始化 HeapInit

void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;}

3.2 堆的销毁 HeapDestory

void HeapDestory(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = php->size = 0;}

3.3 堆的打印 HeapPrint

void HeapPrint(HP* php)
{assert(php);for (size_t i = 0; i < php->size; ++i){printf("%d ", php->a[i]);}printf("\n");
}

3.4 堆的插入元素 HeapPush   *

堆的元素插入是堆的一大重点和难点。接下来我们对该功能进行分析和实现。

功能分析:

1、我们要向堆中插入元素,我们首先要判断数组是否空间已满,如果空间已满就要扩容。扩容后再将新元素插入数组尾部。此过程和顺序表相同。

2、由于插入新元素,我们要对该元素进行分析(此处以如下图小堆举例),分析插入元素是否会破坏堆结构,如果破坏了堆,我们就要对堆进行向上调整。

3、向上调整过程分析(过程步骤如下图):

a. 我们发现出入新元素10之后,10作为28(父节点)的子节点却比28小,这样就破坏了我们的堆结构,这样就不构成小堆。因此我们需要对该结构进行调整。

b.由于堆的物理结构是一个数组,所以我们可以通过数组下标的形式找到我们孩子节点的父节点。不难分析出parent = (child-1)/2.当我们找到父节点时,我们进行大小比较,如果子节点小于父节点,此时就要进行交换元素。再让子节点到父节点的位置,重新计算父节点。

c.持续循环比较,如果child等于0时,说明向上调整结束。因此循环的条件可写为child>0.

注:循环过程中一旦成堆,则跳出循环。

功能实现:

//交换函数
void Swap(HPDataType* pa, HPDataType* pb)
{HPDataType tmp = *pa;*pa = *pb;*pb = tmp;
}//向上调整
void AdjustUp(HPDataType* a, size_t child)
{size_t parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void HeapPush(HP* php, HPDataType x)
{assert(php);//考虑是否扩容if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){printf("realloc failed\n");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;++php->size;//需要向上调整AdjustUp(php->a, php->size - 1);
}

3.5 堆的删除元素 HeapPop  *

删除堆是删除堆顶的数据
思路:将堆顶的数据跟最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

功能分析:

我们要删除堆是删除堆顶的数据,我们不能直接删除堆顶的数据。如果直接删除堆顶的数据,就会破坏堆结构,并且复原的复杂度较高。在这里我们介绍一种方法不仅解决了删除堆的问题,并且复杂度较低。

1、首先我们要将堆顶的数据跟最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。

2、向下调整算法具体步骤(过程步骤如下图):

a.我们将堆顶元素和数组最后一个元素交换后,此时堆顶的元素是数组的最后一个元素,我们要进行向下调整。定义parent为堆顶元素,查找2个子节点中较小的一个节点作为孩子节点。由于堆是数组结构实现,我们可以首先找到左孩子节点child = 2*parent+1。让左孩子和右孩子进行比较,较小的作为child的最后值。

b.如果孩子小于父亲,则交换,并继续往下调整。让parent到child的位置,再重新计算孩子。

c.当孩子大于等于元素总个数时,循环结束。因此循环的条件可以写为child<size.

注:循环过程中一旦成堆,则跳出循环。

功能实现:

void AdjustDown(HPDataType* a, size_t size, size_t root)
{size_t parent = root;size_t child = parent * 2 + 1;//先拿到左孩子while (child < size){// 1、选出左右孩子中小的那个if (child + 1 < size && a[child + 1] < a[child]){++child;}// 2、如果孩子小于父亲,则交换,并继续往下调整if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);--php->size;AdjustDown(php->a, php->size, 0);
}

3.6 判断是否为空 HeapEmpty

bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

3.7 求元素个数

size_t HeapSize(HP* php)
{assert(php);return php->size;
}

3.8 求堆顶元素

HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}

4.堆的应用:堆排序   ***

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆
升序:建大堆
降序:建小堆
2. 利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

假设此时我们需要对数组元素进行升序排序,我们就可以使用我们刚刚实现的小堆。

4.1 堆排序实现过程分析

1、首先我们将数组的元素插入到堆中,根据向上调整,此时堆已经是小堆。

2、根据小堆的性质,堆顶的元素一定是该堆中最小的元素,因此我们取到堆顶的元素,再删除堆顶的元素让堆重新生成小堆。依次循环即可解决升序排序(降序排序只需将小堆改为大堆即可)。

4.2 堆排序实现代码

//堆排序
void HeapSort(int* a, int size)
{HP hp;HeapInit(&hp);for (int i = 0; i < size; ++i){HeapPush(&hp, a[i]);}size_t j = 0;while (!HeapEmpty(&hp)){a[j] = HeapTop(&hp);j++;HeapPop(&hp);}HeapDestory(&hp);
}
int main()
{// TestHeap();int a[] = { 4,2,1,3,5,7,9,8,6};HeapSort(a,sizeof(a)/sizeof(int));for (int i = 0; i < sizeof(a) / sizeof(int); ++i){printf("%d ", a[i]);}return 0;
}

4.3 堆排序结果演示

5.堆(小堆)的完整代码

2022_03_30 -- 堆/2022_03_30 -- 二叉树 · 李兴宇/数据结构 - 码云 - 开源中国 (gitee.com)

(本篇完)

[ 数据结构-C实现 ] 堆、堆排序的分析及实现相关推荐

  1. 数据结构与算法之堆排序

    数据结构与算法之堆排序 目录 堆排序介绍 代码实现 1. 堆排序介绍 堆排序(Heapsort)是指利用堆((英语:heap)是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组 ...

  2. Python天天美味(32) - python数据结构与算法之堆排序

    1. 选择排序 选择排序原理是先选出最小的数,与第一个数交换,然后从第二个数开始再选择最小的数与第二个数交换,-- def selection_sort(data):     for i in ran ...

  3. 大小堆堆排序堆的应用

    一.首先说一下堆的概念吧这里就不按照标准的概念来说了,就说说我理解的堆. 堆就是一个数组中的元素,参考着完全二叉树的这种数据结构存储在数组中,这样就是一个堆.注意:这里是参考,实际的存储还是在数组中, ...

  4. 使用 Eclipse Memory Analyzer 进行堆转储文件分析

    概述 对于大型 JAVA 应用程序来说,再精细的测试也难以堵住所有的漏洞,即便我们在测试阶段进行了大量卓有成效的工作,很多问题还是会在生产环境下暴露出来,并且很难在测试环境中进行重现.JVM 能够记录 ...

  5. 数据结构与算法--代码鲁棒性案例分析

    代码鲁棒性 鲁棒是robust的音译,就是健壮性.指程序能够判断输入是否符合规范,对不合要求的输入能够给出合理的结果. 容错性是鲁棒的一个重要体现.不鲁棒的代码发生异常的时候,会出现不可预测的异常,或 ...

  6. 数据结构与算法--代码完整性案例分析

    确保代码完整性 在撸业务代码时候,经常面对的是接口的设计,在设计之初,我们必然要先想好入参,之后自然会有参数的校验过程,此时我们需要把可能的输入都想清楚,从而避免在程序中出现各种纰漏.但是难免面面俱到 ...

  7. jmap, jhat, jvisualvm:java堆内存对象分析利器

    转载自 jmap, jhat, jvisualvm:java堆内存对象分析利器 jmap -help查看命令用法. jmap -heap <pid> 查看堆使用情况. jmap -dump ...

  8. java eden space_《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读...

    堆内存使用分析,垃圾收集器 GC 日志解读 重要的东东 在Java中,对象实例都是在堆上创建.一些类信息,常量,静态变量等存储在方法区.堆和方法区都是线程共享的. GC机制是由JVM提供,用来清理需要 ...

  9. 转载:WPS 2012/2013 RTF fchars 堆溢出漏洞分析

    WPS 2012/2013 RTF fchars 堆溢出漏洞分析 On 2013年12月11日, in 安全分析, by code_audit_labs by phperl ,zzf,nine8 of ...

  10. 深入理解Java虚拟机——虚拟机堆转储快照分析工具(jhat)

    目录 一.虚拟机堆转储快照分析工具(jhat)的概述 二.实际工作中一般不直接使用jhat命令分析dump文件原因 三.使用jhat分析dump文件示例 一.虚拟机堆转储快照分析工具(jhat)的概述 ...

最新文章

  1. OSPF 疑重难要14点--转屎壳Q岛的一个文章
  2. python 多线程 类_Python中如何自定义一个多线程类呢?
  3. PhpForm表单验证
  4. bzoj 2435: [Noi2011]道路修建【树形dp】
  5. Factorized TDNN(因子分解TDNN,TDNN-F)
  6. PHP的- = :: self $this
  7. 【C语言】C语言中一些零碎的基础知识
  8. Postgres psql: 致命错误: 角色 “postgres“ 不存在
  9. 怎么锁定电脑屏幕_锁定流行趋势,信霆为你盘点3C数码配件中的人气单品
  10. 今日你以老师为荣,明日老师以你为荣!
  11. VMware-workstation 密钥
  12. Spring框架实战入门(超全面,超实用)
  13. 第二课:更换国内下载源(阿里源为例)
  14. Python学习笔记—— 面向对象1. 面向对象基础
  15. 【大气红歌】著名民通歌唱家拉齐的音乐之路
  16. 读《明朝那些事》有感
  17. 数据重生:让神经机器翻译中的不活跃样本“复活”
  18. 2022腾讯实习生移动客户端开发一面(IEG)
  19. html自动生成拼音五笔,如何根据单元格汉字自动生成拼音码和五笔码
  20. Fusion 360 最新动态 - 温度场和热应力分析

热门文章

  1. Nagios安装与部署
  2. 安装Fiddler后浏览器请求被拦截无法上网问题
  3. 如题,如何在IDEA的settings属性中,通过设置快捷键实现字符串中的英文字母转大小写?
  4. 微信朋友圈第五条广告是怎么出现在朋友圈当中的
  5. 【设计模式 - 模板方法】
  6. ENVI中基于Modis影像提取NDVI处理步骤
  7. Python统计学实例之正态分布:计算男女身高相差>5厘米的概率
  8. yolov5最新版本预训练模型下载
  9. 查看电脑支持多大内存条方法:
  10. 每月一书(202302)《狂飙》