当前位置: 首页 > news >正文

堆的基本实现

一、堆的概念

在提出堆的概念之前,首先要了解二叉树的基本概念

一颗二叉树是节点的有限集合,该集合:

1、或者为空;

2、或者由一个根节点加上两颗分别称为左子树和右子树的两颗子树构成;

 堆就是一颗完全二叉树;

堆有两种:小堆和大堆

堆在内存上的存储是数组形式的(物理结构);我们认为想象成链式结构(逻辑结构)

通过数组结构去实际存储,有这样的规律:每个父节点的下标乘以2加1就是左孩子节点,加2就是右孩子节点;无论是左孩子还是右孩子,其下标减去1再 /2 就是父节点的下标,这就是通过数组存储堆(完全二叉树)的优点。


 

 二、堆实现的相关头文件

#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
#include<errno.h>
//堆有两种:大堆、小堆
typedef int HPDataType;typedef struct Heap
{HPDataType* arr;int size;int capacity;
}Heap;//堆的构建
void HeapInit(Heap* php);//堆的销毁
void HeapDestroy(Heap* php);//向上调整
void AdjustUp(Heap* php, int child);
//堆的插入
void HeapPush(Heap* php, HPDataType x);//向下调整
void AdjustDown(Heap* php, int parent, int size);
//堆的删除
void HeapPop(Heap* php);//取出堆顶的数据
HPDataType HeapTop(Heap* php);//堆的数据个数
int HeapSize(Heap* php);//堆的判空
bool HeapEmpty(Heap* php);

堆是完全二叉树,所以用数组存储可以方便访问子节点和父节点;


三、堆基本接口的实现(大堆)

1、堆的构建(初始化)

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

2、堆的销毁

//堆的销毁
void HeapDestroy(Heap* php)
{assert(php);if (php->arr == NULL){free(php->arr);}php->arr = NULL;php->size = php->capacity = 0;
}

3、堆的插入

void HeapPush(Heap* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc failed");exit(1);}php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size++] = x;//向上调整AdjustUp(php->arr,php->size-1);
}

堆只有大堆和小堆之分,插入数据和顺序表一样,关键是插入数据之后要对数组里面的数据进行调整;

插入数据用到向上调整

//向上调整
void AdjustUp(HPDataType* arr,int child)
{int parent = (child - 1) / 2;while (child>0){if (arr[child] > arr[parent]){//交换数组里面的值Swap(&arr[child], &arr[parent]);//继续比较大小要将child和parent的值向上移动child = parent;parent = (child - 1) / 2;}else{//之前的数据都是一个一个建好的大堆//父节点的值比子节点的大,停止break;}}
}
void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}

每次插入进堆的数据都是孩子节点(形参名称),向上调整的原因就是每个子树的父亲节点理论上是要大于孩子节点的,但是插入的数据可能要比父节点大,所以在向上调整函数里面要先通过孩子节点的下标找到对应的父节点,之后再比较看是否要交换,直到父亲节点大于子节点;

循环结束的条件是child>0,当child为0的时候说明已经调整到根节点的位置了。

4、堆的删除

//堆的删除
void HeapPop(Heap* php)
{assert(php && php->size>0);//删除是删除的是堆顶的数据,若是直接让数组整体往前移动 堆被打乱 要重新建堆 时间复杂度高;//方法:交换堆首位的数据,让size--,再从堆顶开始向下调整Swap(&php->arr[0],& php->arr[php->size - 1]);php->size--;AdjustDown(php->arr, 0, php->size - 1);
}

堆的删除删除的是根节点的数据,也就是数组里面下标为0的数据;如果直接移动数组下标删除,那么新数组就不再是一个堆,此时要重新建堆,代价过大;

采用这种方式:每次进行删除之前先交换堆首尾的数据,之后再让size--,就访问不到原来的根节点,此时得到的新数组,除了根节点处的数据,它的子树都是大堆;此时进行向下调整算法,把这个不应该是根节点的数据向下调整,把下面的数据里面最大的调整到根节点处;

向下调整算法

void AdjustDown(HPDataType* arr, int parent, int size)//size指向的是最后一个有效数据
{int child = parent * 2 + 1;//定义为左孩子while (child <= size){if (child+1<=size && arr[child] < arr[child + 1])//当最后一个子树只有左孩子时,child+1越界{child = child + 1;//若是右孩子较大,那么就定义成右孩子}//总是大的调到上面去if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

向下调整是从下标为0处开始调整的,这个0下标位置就是父节点,通过乘以2加上1的办法找到子节点,但是要找到子节点里面较大的那个所以上面代码就有了child处和child+1处数据大小的比较,若是父节点小于子节点就交换;每次调整完都刷新父节点和子节点的下标,以便一直往下面调整直到父节点的数据要大于子节点的数据就停止;

循环结束的条件是child<size,这里的size是数组里面最后一个有效数的下标;

5、堆顶数据、堆的数据个数、堆的判空

//取出堆顶的数据
HPDataType HeapTop(Heap* php)
{assert(php && php->size>0);return php->arr[0];
}//堆的数据个数
int HeapSize(Heap* php)
{assert(php);return php->size;
}//堆的判空
bool HeapEmpty(Heap* php)
{assert(php);return php->size == 0;
}

相关文章:

  • mysql中提供的函数
  • 独孤思维:长线副业,越做越香
  • C语言常见字符函数和字符串函数精讲
  • connect的非阻塞模式
  • Discourse 如何通过终端工具访问 PGSQL
  • 多模态
  • Android APP 音视频(02)MediaProjection录屏与MediaCodec编码
  • java找不到符号解决办法
  • 《Programming from the Ground Up》阅读笔记:p75-p87
  • css更改图片颜色
  • ReadAgent,一款具有要点记忆的人工智能阅读代理
  • Vue3点击按钮实现跳转页面并携带参数
  • openFeign配置okhttp
  • 63.利用PEB获取模块列表
  • Hive小文件合并
  • ES6指北【2】—— 箭头函数
  • JavaScript 如何正确处理 Unicode 编码问题!
  • CentOS 7 防火墙操作
  • Docker入门(二) - Dockerfile
  • Golang-长连接-状态推送
  • HashMap ConcurrentHashMap
  • javascript数组去重/查找/插入/删除
  • Java多态
  • Mybatis初体验
  • Node项目之评分系统(二)- 数据库设计
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 回流、重绘及其优化
  • 计算机在识别图像时“看到”了什么?
  • 将回调地狱按在地上摩擦的Promise
  • 前端工程化(Gulp、Webpack)-webpack
  • 如何使用 JavaScript 解析 URL
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 网页视频流m3u8/ts视频下载
  • 线上 python http server profile 实践
  • 一些css基础学习笔记
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 【云吞铺子】性能抖动剖析(二)
  • HanLP分词命名实体提取详解
  • hi-nginx-1.3.4编译安装
  • ​字​节​一​面​
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • !$boo在php中什么意思,php前戏
  • #14vue3生成表单并跳转到外部地址的方式
  • #Lua:Lua调用C++生成的DLL库
  • #mysql 8.0 踩坑日记
  • #Z2294. 打印树的直径
  • #数据结构 笔记一
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (12)目标检测_SSD基于pytorch搭建代码
  • (bean配置类的注解开发)学习Spring的第十三天
  • (day 12)JavaScript学习笔记(数组3)