【完全二叉树魔法:顺序结构实现堆的奇象】(上)

简介: 【完全二叉树魔法:顺序结构实现堆的奇象】

本章重点


  • 二叉树的顺序结构
  • 堆的概念及结构
  • 堆的实现
  • 堆的调整算法
  • 堆的创建
  • 堆排序
  • TOP-K问题


1.二叉树的顺序结构


       普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。


访问结点的规律:

//访问孩子节点
leftchild = parent*2+1
rightchild = parent*2+2
//访问父亲结点
parent = (child-1)/2


2.堆的概念及结构



堆的性质:


  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
  • 大堆:任何父亲节点 >= 孩子结点
  • 小堆:任何父亲节点 <= 孩子结点


1.下列关键字序列为堆的是:()。

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

解析:

       堆(Heap)是一种特殊的树形数据结构,它通常有两种类型:小堆(Min Heap)和大堆(Max Heap)。在小堆中,父节点的值小于或等于其子节点的值,而在大堆中,父节点的值大于或等于其子节点的值。

       要判断一个序列是否是堆,需要检查该序列是否满足堆的性质。我们发现A符合大堆的性质父节点的值大于或等于其子节点的值。


2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建小堆,在此过程中,关键字之间的比较次数是()。

A 1

B 2

C 3

D 4

解析:

  在一个小根堆中删除根节点后,需要重新构建小根堆。删除根节点后,通常会将堆的最后一个元素移动到根的位置,然后通过与其子节点的比较来逐级下移,以确保小根堆的性质得以恢复。


       给定的小根堆是:8, 15, 10, 21, 34, 16, 12。

       首先删除根节点8后,将最后一个元素12移到根的位置,得到:12, 15, 10, 21, 34, 16。

       然后,我们需要逐级下移12,直到小根堆性质得以恢复。在这个过程中,我们将12与其子节点进行比较,选择较小的子节点来交换位置。

       第一次比较:12与15比较,不需要交换。

       第二次比较:12与10比较,需要交换。

       第三次比较:12与16比较,不需要交换。

       因此,关键字之间的比较次数是3次。


3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为()。

A(11 5 7 2 3 17)

B(11 5 7 2 17 3)

C(17 11 7 2 3 5)

D(17 11 7 5 3 2)

E(17 7 11 3 5 2)

F(17 7 11 3 2 5)

       堆排序是一种基于堆数据结构的排序算法,通常会建立一个最大堆(Max Heap)或最小堆(Min Heap)来进行排序。在这里,我们需要建立一个最大堆。

       初始堆的建立过程通常是从数组的末尾开始,逐步将元素向上移动,以满足堆的性质。对于给定的排序码数组(5 11 7 2 3 17),初始堆的建立步骤如下:


4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()。

A[3,2,5,7,4,6,8]

B[2,3,5,7,4,6,8]

C[2,3,4,5,7,8,6]

D[2,3,4,5,6,7,8]


3.堆的实现


       这里的堆是使用数组实现的,博主重点介绍堆的删除和插入接口,其他接口同顺序表相同,这里就不过多赘述了。

typedef int HPDataType;
typedef struct Heap
{
  HPDataType* a;
  int size;
  int capacity;
}HP;
// 堆的初始化
void HeapInit(HP* php);
// 堆的打印
void HeapPrint(HP* php);
// 堆的销毁
void HeapDestroy(HP* php);
//堆的创建
void HeapInitArray(HP*php, int* a, int n);
// 堆的插入
void HeapPush(HP* php, HPDataType x);
// 堆的删除
void HeapPop(HP* php);
// 取堆顶的数据
HPDataType HeapTop(HP* php);
// 堆的数据个数
int HeapSize(HP* php);
// 堆的判空
bool HeapEmpty(HP* php);


3.1堆的插入:void HeapPush(HP* php, HPDataType x)


  1. 先将元素插入到堆的末尾,即最后一个孩子之后。
  2. 插入之后如果堆的性质遭到破坏,将信新插入节点顺着其双亲往上调整到合适位置即可,即向上调整。
  3. 向上调整结束的条件是child等于0,parent等于-1,但是我们写的循环结束条件是child大于0,因为parent的值不会是-1,而是0,这里可以去看我的另外一篇文章,里面介绍了c语言取整规则:链接

void Swap(HPDataType* p1, HPDataType* p2)
{
  HPDataType temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}
//向上调整
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* temp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
    if (temp == NULL)
    {
      perror("realloc fail");
      exit(-1);
    }
    php->a = temp;
    php->capacity = newCapacity;
  }
  php->a[php->size] = x;
  php->size++;
  AdjustUP(php->a, php->size - 1);
}


3.2堆的删除:void HeapPop(HP* php)


  1. 将堆顶元素与堆中最后一个元素进行交换。
  2. 删除堆中最后一个元素。
  3. 将堆顶元素向下调整到满足堆特性为止。
  1. 向下调整的结束条件是child等于叶子结点。
void Swap(HPDataType* p1, HPDataType* p2)
{
  HPDataType temp = *p1;
  *p1 = *p2;
  *p2 = temp;
}
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
  int child = parent * 2 + 1;
  //parent到叶子结点就结束
  while (child < n)
  {
    //可能不存在右孩子
    if (child + 1 < n && a[child] > a[child + 1])
    {
      child++;
    }
    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);
}


【完全二叉树魔法:顺序结构实现堆的奇象】(中):https://developer.aliyun.com/article/1424933

相关文章
|
人工智能 搜索推荐 算法
玩转通义星尘:体验定制化多样角色能力
在杭州云栖大会上,阿里云对外展示了一款个性化角色创作平台——**通义星尘**,其基于大规模高质量个性化对话数据,采用分阶段的个性化训练策略,使得模型在保持通用能力的基础上,延伸出拟人、具有情感、鲜明语言风格的能力,在角色的个性、风格遵循上具有更强的指令遵循能力。那么其能力展现到底如何?我们又能玩出哪些花样呢?今天开始测试通义星尘,争取年前把8个垂直模型都测试一遍,,加油!本文为原创,未经许可请勿搬运。
玩转通义星尘:体验定制化多样角色能力
|
计算机视觉
OpenCV(三十八):二维码检测
OpenCV(三十八):二维码检测
1262 0
|
编解码 算法 数据处理
基于八叉树的空间划分及搜索操作
基于八叉树的空间划分及搜索操作
基于八叉树的空间划分及搜索操作
|
5月前
|
人工智能 JavaScript API
【保姆级教程】一键搭建AI协作团队!阿里云/本地搭建OpenClaw通用多Agent框架(开发/投资/内容)+Coding Plan配置指南
2026年,OpenClaw的多Agent玩法已从“单一团队搭建”升级为“多团队并行运作”——越来越多用户需要同时推进多个项目(如一边开发软件,一边做投资分析),但传统手动配置Agent的方式存在三大痛点:重复踩坑、配置混乱、团队隔离困难。而Multi-Agent Dev Team v2.2技能的推出,彻底解决了这些问题:它不是特定场景的脚手架,而是通用的多AI代理协作框架,通过交互式向导,可快速搭建任意类型的专业团队,支持多团队并行运行,还解决了子代理会话超时的治理难题。
1306 4
|
Java 关系型数据库 MySQL
记录解决一个小错误:Establishing SSL connection without server‘s identity verification is not recommended
本文记录了解决Spring Boot连接MySQL时出现的SSL连接警告问题的步骤,通过在数据源配置中添加`useSSL=false`来禁用SSL连接。
|
10月前
|
人工智能 运维 安全
Arista VeloCloud SD-WAN 6.4 - 领先的 SD-WAN 解决方案
Arista VeloCloud SD-WAN 6.4 - 领先的 SD-WAN 解决方案
314 2
Arista VeloCloud SD-WAN 6.4 - 领先的 SD-WAN 解决方案
|
9月前
|
传感器 运维 监控
中小容量电力设备选型指南:计量型免维护呼吸器的核心价值
针对35kV-500kV中小容量变压器等设备,计量型免维护呼吸器集成高效吸湿、实时监测与智能预警功能,标配工业显示屏与RS485通讯,支持远程监控与系统接入,实现防潮运维从“被动处置”到“主动防控”升级,适配多种环境与协议,降低运维成本,提升供电可靠性。(239字)
396 0
|
存储 人工智能 自然语言处理
AI智能体从请求到响应,这系统过程中究竟藏着什么?
三桥君带你深入解析AI智能体从用户请求到生成响应的全流程,涵盖接入服务、智能体应用、知识检索、模型重排、LLM调用与工具执行等关键技术环节,揭开AI背后的运作原理。
665 2
|
缓存 前端开发 JavaScript
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
890 1
|
计算机视觉
DeprecationWarning
DeprecationWarning: ANTIALIAS is deprecated and will be removed in Pillow 10 (2023-07-01). Use LANCZOS or Resampling.LANCZOS instead.
1087 1