数据结构从入门到精通(第六篇) :堆的实现

简介: 数据结构从入门到精通(第六篇) :堆的实现

堆的概念

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储

在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1,

2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。


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

image.png


堆的实现(大堆)

接口

//堆初始化
void HeapInit(HP* hp);
//堆销毁
void HeapDestroy(HP* hp);
//入堆
void HeapPush(HP* hp, HPDataType x);
//出堆
void HeapPop(HP* hp);
//堆数据打印
void HeapPrint(HP* hp);
//堆顶数据
HPDataType HeapTop(HP* hp);
//堆存入数据个数
int HeapSize(HP* hp);
// 堆的判空
bool HeapEmpty(HP* hp);
//交换函数
void Swap(HPDataType* a, HPDataType* b);
//数据向上调整
void AdjustUp(HPDataType* a, int child);
//数据向下调整
void AdjustDown(HPDataType* a, int size, int parent);


堆结构创建


下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算

法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的

子树开始调整,一直调整到根节点的树,就可以调整成堆。

//默认堆中的数据类型
typedef int HPDataType;
//堆结构体类型
typedef struct Heap
{
  HPDataType* a;//数组指针(指向动态开辟的空间)
  int size;//堆中存放的数据个数
  int capacity;//堆的容量(数组长度)
}HP;

image.png


堆的初始化

//堆初始化
void HeapInit(HP* hp)
{
  assert(hp);//避免传入参数错误
  //初始化
  hp->a = NULL;
  hp->size = hp->capacity = 0;
}


堆的销毁

//堆销毁
void HeapDestroy(HP* hp)
{
  assert(hp);//避免传入参数错误
  //释放
  free(hp->a);
    hp->a=NULL;//置空
  hp->capacity=hp->size=0;
}


入堆


这里采用的入堆方式是现将入堆数据先放在堆存储数据最尾部的后一个位置, 再从这个位置进行向上调整,直到符合大堆的存储要求


//入堆
void HeapPush(HP* hp, HPDataType x)
{
  assert(hp);//避免传入参数错误
  //满堆的情况
  if (hp->size == hp->capacity)
  {
  //如果容量为0则开辟4个空间,否则扩展成原来的两倍
  int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
  HP* tmp = (HP*)realloc(hp->a, sizeof(HP) * newcapacity);
  if (tmp == NULL)//开辟失败则打印错误并结束进程
  {
    perror("realloc fail:");
    exit(-1);
  }
  hp->capacity = newcapacity;//更新数据
  hp->a = tmp;
  }
  //入堆操作
  hp->a[hp->size] = x;//先放入尾端,再调整
  hp->size++;
  //数据调整
  AdjustUp(hp->a, hp->size - 1);//传入数组地址和下标
}


堆向上调整

依据父子节点位置,找到对应下标进行比较数据

如果数据不符合大堆则进行交换,直到交换成符合大堆

当入堆的数据到下标为0时或者符合大堆时停止交换


image.png

代码:

//交换函数
void Swap(HPDataType* a, HPDataType* b)
{
  HPDataType tmp = *a;
  *a = *b;
  *b = tmp;
}
//数据调整
void AdjustUp(HPDataType* a, int child)//
{
  int parent = (child - 1) / 2;
  while (child)
  {
  if (a[parent] < a[child])//不符合情况交换
    Swap(&a[parent], &a[child]);
  else
    break;
  //调整下标
  child = parent;
  parent = (child - 1) / 2;
  }
}


堆的pop

出堆方式:

首先我们将堆顶数据也就是下标为0的数据与堆尾数据交换

交换后让记录存入数据的变量减减,实现删除堆顶数据

再让现在堆顶的数据向下调整


代码:

//出堆(删除堆顶的数据)
void HeapPop(HP* hp)
{
  assert(hp);//避免传入参数错误
  assert(hp->size);//空堆的情况
  Swap(&hp->a[0], &hp->a[hp->size - 1]);//先将堆顶数据与堆尾交换
  hp->size--;//再将记录数据个数变量减减实现删除的效果
  //对现在堆顶的数据进行下调
  AdjustDown(hp->a, hp->size, 0);
}


向下调整数据

同样的依据父子节点位置,找到对应下标进行比较数据

因为堆是一个完全二叉树,需要考虑存在只有左子节点没有右子节点的情况

如果左右子节点都存在,那么与左右子节点中数据大的节点进行比较

如果数据不符合大堆则进行交换,直到交换成符合大堆

当比较的子树下标超出存储数据个数时或者符合大堆时停止交换

代码:

//数据调整
void AdjustDown(HPDataType* a, int size, int parent)
{
  int child = parent * 2 + 1;
  while (child<size)
  {  
  //找到数据大的儿子
  if (child + 1 < size && a[child + 1] > a[child])
  {
    child++;
  }
  //将父节点与大子节点交换
  if (a[child] > a[parent])
  {
    Swap(&a[child], &a[parent]);//交换数据
    parent = child;//调整下标位置
    child = parent * 2 + 1;
  }
  else
  {
    break;//结束调整
  }
  }
}


建堆时间复杂度的计算

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的

就是近似值,多几个节点不影响最终结果):


image.png


建堆的时间复杂度为O(N)。


总结

本篇主要讲的是堆的实现和结构特点

但对堆的具体性质和用法 暂未讲解

堆的具体性质和用法会放在下一篇堆的应用来讲解,敬请期待吧 b( ̄▽ ̄)d


相关文章
|
4月前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
70 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
4月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
173 16
|
5月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
201 1
|
5月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
5月前
|
存储 机器学习/深度学习 算法
探索数据结构:入门及复杂度的解锁
探索数据结构:入门及复杂度的解锁
|
5月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
68 0
|
5月前
|
存储 算法
【数据结构】二叉树——顺序结构——堆及其实现
【数据结构】二叉树——顺序结构——堆及其实现
|
5月前
【数据结构】大根堆和小根堆
【数据结构】大根堆和小根堆
127 0
|
5月前
|
存储 缓存 应用服务中间件
Nginx入门 -- 基本数据结构中之ngx_hash_t
Nginx入门 -- 基本数据结构中之ngx_hash_t
58 0
|
5月前
|
存储 缓存 应用服务中间件
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
60 0