堆的实现(C语言版)

在这里插入图片描述

文章目录

  • 概述
  • 堆的实现
    • 初始化
    • 销毁
    • 插入
    • 删除
    • 取堆顶元素
    • 求堆的长度
    • 判断堆是否为空
  • 完整代码

概述

如果有一个关键码的集合K = {k0,k1,k2…kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <=K2*i+1 且 Ki<=K2*i+2 (Ki >= K2*i+1且 Ki>= K2*i+1) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。堆的性质:

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

在这里插入图片描述

堆的实现

初始化

堆的存储结构是一个数组,堆的初始化需要定义一个数组,当前元素个数和容量。和顺序表的初始化一样。

typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}

销毁

释放数组a的空间,将php->capacity = php->size = 0

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

插入

堆的插入是先在数组的最后插入元素,但是需要满足堆的特点(大堆或小堆),因此需要用到向上调整算法,来实现这一特点。

介绍向上调整算法

这里小编以实现小堆为例

在数组的最后插入一个元素child,然后这个元素与其双亲节点parent进行比较:

  • 如果 child>parent:满足小堆的条件,无需交换
  • 如果 child<parent:不满足小堆条件,此时需要将孩子节点child与它的双亲结点parent进行交换,此时原来的双亲结点parent变成了孩子结点child,原来的孩子节点child变成了双亲结点parent。此时,再让现在的双亲结点parent和它的双亲结点parent进行比较,如果不满足小堆,则继续交换,继续比较
  • 循环结束的条件是child>0

举个例子:

如下,在堆中插入元素10:

在这里插入图片描述
将10与它的双亲结点进行比较,10<28,不满足小堆的条件,将10和28,进行交换:

在这里插入图片描述

交换完后,此时的10变成了28的双亲结点,28变成了10的孩子结点。现在再将10与它的双亲结点比较,10<18,不满足小堆的特点,继续交换。

在这里插入图片描述

交换完后10变成了18的双亲结点,18变成了10的孩子结点。10和它的双亲结点比较,依然不满足小堆条件,继续交换

在这里插入图片描述

此时,10已经变成了根节点,并且满足小堆的条件,循环结束。

看了图解,对向上调整算法有了大概的印象,但是代码的编写,还需要再去分析一下。

定义parent是孩子的双亲结点,双亲结点parent与孩子结点child满足parent = (child - 1) / 2关系。进入循环,比较孩子节点的值和双亲结点的值,判断是否满足小堆的条件。

void swap(HPDataType* p1, HPDataType* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

写完向上调整算法,便可实现插入操作

void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}

删除

在删除操作里面,一般规定删除堆顶,即根节点

在这里插入图片描述

删除根节点的常规操作是将根结点和最后一个叶节点进行交换,然后尾删即可,此时根节点的左右子树依然是小堆

在这里插入图片描述
但是根节点不满足小队的条件,因此引入向下调整算法

向下调整算法:

向上调整算法是一个道理

但是此时根节点是双亲结点,有两个孩子,不知道该选择哪一个孩子。这里使用到了假设法:假设左孩子小,如果假设错了,更新一下

判断双亲结点和孩子结点的大小:

  • 如果双亲结点小于孩子结点,直接结束
  • 如果双亲结点大于孩子结点,交换双亲结点和孩子结点的值,然后更新一下双亲结点的位置和孩子节点的位置

循环结束的条件是child<size

和向上调整算法基本一致,直接上代码:

AdjustDown(HPDataType* a,int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (a[child] > a[child + 1] && child + 1 < size){child++;}if (a[child] < a[parent]){swap(a[parent], a[child]);parent = child;child = child * 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);
}

取堆顶元素

先判断堆是否存在,然后直接返回堆顶元素即可

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

求堆的长度

先判断堆是否存在,直接返回堆的长度即可

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

判断堆是否为空

先判断堆是否存在,如果php->size==0,那么堆为空,返回true,反之返回false

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

完整代码

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
// 规定删除堆顶(根节点)
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);

Heap.c

# define _CRT_SECURE_NO_WARNINGS#include"Heap.h"void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->capacity = php->size = 0;
}void swap(HPDataType* p1, HPDataType* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}AdjustDown(HPDataType* a,int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (a[child] > a[child + 1] && child + 1 < size){child++;}if (a[child] < a[parent]){swap(a[parent], a[child]);parent = child;child = child * 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);
}HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}size_t HeapSize(HP* php)
{assert(php);return php->size;
}bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/167031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python Opencv实践 - 全景图片拼接stitcher

做一个全景图片切片的程序Spliter 由于手里没有切割好的全景图片资源&#xff0c;因此首先写了一个切片的程序spliter。 如果有现成的切割好的待拼接的切片文件&#xff0c;则不需要使用spliter。 对于全景图片的拼接&#xff0c;需要注意一点&#xff0c;各个切片图片之间要有…

NX二次开发UF_CSYS_map_point 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_map_point Defined in: uf_csys.h int UF_CSYS_map_point(int input_csys, double input_point [ 3 ] , int output_csys, double output_point [ 3 ] ) overview 概述 Ma…

Android11编译第七弹:串口文件读写

问题&#xff1a;需要对SIM卡进行管理&#xff0c;支持APP切换SIM卡。此功能需要访问串口文件&#xff0c;并且对串口文件进行读写。APP操作串口文件/dev/ttyUSB1时&#xff0c;串口文件打开失败。 2023-11-23 10:59:44.092 14264-14264 MULTI_CARD_SerialHandle com.wellnkio…

三分钟快速理解 ChatGPT 背后的大模型技术

在过去的十年中&#xff0c;人工智能领域取得了重大突破&#xff0c;其中自然语言处理&#xff08;NLP&#xff09;是其重要子领域之一。NLP使用的模型之一是大型语言模型&#xff08;LLMs&#xff09;。LLMs被设计用于处理大量文本数据&#xff0c;采用先进的神经网络架构&…

鼠标拖拽问题,不选中文本不触发单击事件

文章目录 1. 为什么鼠标单击的时候触发了mousemove事件&#xff1f;明明鼠标没有移动2. 鼠标拖拽元素怎么能不触发单击事件&#xff1f;怎么处理鼠标在元素内的相对定位&#xff0c;而不是每次定位到左上角&#xff1f;方式一&#xff1a;拖拽的元素没有注册click监听就不会触发…

10年测试老鸟,自动化测试经验10条建议,一路狂飙...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、哪一刻&#x…

Postman如何使用(二):Postman Collection的创建/使用/导出分享等

一、什么是Postman Collection&#xff1f; Postman Collection是可让您将各个请求分组在一起。 您可以将这些请求组织到文件夹中。中文经常将collection翻译成收藏夹。如果再下文中看到这样的翻译不要觉得意外。Postman Collection会使你的工作效率更上一层楼。Postman Colle…

教师工作就业前景

在这个知识爆炸的时代&#xff0c;当老师无疑是社会发展的重要基石。随着科技的进步和社会的发展&#xff0c;教育行业的需求也在不断增长。那么&#xff0c;教师工作的就业前景如何呢&#xff1f; 我们来看看教师工作的市场需求。随着国家对教育的重视和投入的增加&#xff0c…

C/C++ 实现Socket交互式服务端

在 Windows 操作系统中&#xff0c;原生提供了强大的网络编程支持&#xff0c;允许开发者使用 Socket API 进行网络通信&#xff0c;通过 Socket API&#xff0c;开发者可以创建、连接、发送和接收数据&#xff0c;实现网络通信。本文将深入探讨如何通过调用原生网络 API 实现同…

「Java开发中文指南」IntelliJ IDEA插件安装(一)

IntelliJ IDEA是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的Java开发工具&#xff0c;尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能是非常强大的。 插件扩展了Intel…

【分布式】分布式中的时钟

一、物理时钟 vs 逻辑时钟 时钟的存在主要是为了标识事件的发生顺序。 分布式系统不使用物理时钟记录事件&#xff0c;分布式系统中每个节点记录的时间并不一样&#xff0c;即使设置了 NTP 时间同步节点间也存在毫秒级别的偏差 所以需要有另外的方法记录事件顺序关系&#x…

vue2中使用echarts

1,安装echarts npm install --save echarts 2&#xff0c;具体页面 <template><div class"app-container"><div class"aa" id"main" style"width: 500px; height: 400px;"></div></div> </te…

PDF 批量处理软件BatchOutput PDF mac中文版介绍

BatchOutput PDF mac是一款适用于 Mac 的 PDF 批量处理软件。它可以帮助用户将多个 PDF 文件进行异步处理&#xff0c;提高工作效率。 BatchOutput PDF 可以自动化执行许多任务&#xff0c;包括 PDF 文件的打印、转换、分割、压缩、加密、重命名等&#xff0c;而且它还可以将自…

Elasticsearch知识

目录 Elasticsearch逻辑设计和物理设计 逻辑设计物理设计Elasticsearch原理 倒排索引文档的分析过程保存文档搜索文档写数据的底层原理 数据刷新&#xff08;fresh&#xff09;事务日志的写入ES在大数据量下的性能优化 文件系统缓存优化数据预热文档&#xff08;Document&…

【数据分享】2023年我国省市县三级的瞪羚企业数量(免费获取/Excel/Shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平&#xff01;比如一个城市的金融企业较多&#xff0c;那这个城市的金融产业肯定比较发达&#xff1b;一个城市的制造业企业较多&#xff0c;那这个城市的制造业肯定比较发达。 之前我们给大家分享了…

《opencv实用探索·二》根据RGB的像素排列来理解图像深度、像素深度和位深度

通常对于RGB图像主要分为RGB16&#xff0c;RGB24和RGB32。RGB16从高位到低位的排列为R->G->B&#xff0c;RGB24和RGB32从高位到低位的排列为B->G->R。 RGB16: 16 位为一个存储单元&#xff08;一个像素&#xff09;&#xff0c;来存储一个RGB像素;因为人眼对绿色比…

社区物联网云服务架构设计

文章目录 1 摘要2 架构图2.1 社区物联网云服务网络拓扑图2.2 社区物联网云服务通讯流程图2.3 社区远程开锁功能流程图 3 应用场景 1 摘要 随着社区管理越来越智能化&#xff0c;社区物联网升级与改造的市场空间也越来越大。社区物联网包含楼宇对讲、门禁门锁、通道闸等等设备系…

Netty 模型理解

参考文章 1 参考文章 2 官网API文档 Reactor模型 Netty模型 Netty主要基于主从Reactor多线程模型进行了一定的修改&#xff0c;该模型包括以下几个组件&#xff1a; MainReactor&#xff08;主Reactor&#xff09;&#xff1a;负责处理客户端的连接请求。它监听服务器上的端口…

中电金信:守【政】创新,探路保险数字化转型“新范式”

11月23日&#xff0c;CIIP2023中国保险科技创新合作大会在京举办。大会汇集保险科技领域行业专家、学者、国内外头部险企及保险科技公司负责人等各界人士&#xff0c;立足保险行业高质量发展和创新驱动理念&#xff0c;寻找行业数字化转型新动能、新视角&#xff0c;为保险科技…

python中range函数的用法

range() 是Python的一个内置函数。语法格式为&#xff1a;range(start, stop, step) start是初始值&#xff0c;stop是最终值&#xff0c;step是步长。range()函数仅适用于整数&#xff0c;所有参数都必须是整数。步长值可以为正数或负数&#xff0c;不得为零。使用range函数时…