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

数据结构初阶:二叉树(二)

本篇博客主要讲解二叉树---堆的相关知识。

 1.实现顺序结构二叉树

一般堆使用顺序结构的数组来存储数据,堆是一种特殊的二叉树具有二叉树的特性的同时,还具备其他的特性

1.1 堆的概念和结构

 堆具有以下性质:

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

二叉树性质: 

 对于具有 n 个结点的完全⼆叉树,如果按照从上至下从左右的数组顺序对所有结点从 0 开始编号,则对于序号为 i 的结点有:

  1.  若 i>0 , i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,无双亲结点
  2.  若 2i+1,左孩子序号: 2i+12i+1>=n 否则无左孩子
  3.  若 2i+2,右孩子序号: 2i+22i+2>=n 否则无右孩子

1.2 堆的实现 

堆底层结构为数组,因此定义堆的结构为: 

 1.2.1 头文件的包含(Heap.h)
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//堆的结构
typedef int HPDataType;
typedef struct Heap
{HPDataType* arr;int size;      //有效数据个数int capacity;  //空间大小
}HP;//默认初始化堆
void HPInit(HP* php);
//堆的销毁
void HPDestroy(HP* php);
void HPPrint(HP* php);
void Swap(int* x, int* y);//向上调整算法
void AdjustUp(HPDataType* arr, int child);
//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n);//堆的插入
void HPPush(HP* php, HPDataType x);
//删除堆顶的数据
void HPPop(HP* php);
//堆的删除
HPDataType HPTop(HP* php);//判空
bool HPEmpty(HP* php);
 1.2.3 堆的初始化
void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}

代码逻辑: 

定义了一个`HPInit`的函数,用于初始化一个`HP`类型的指针`php`。函数内部首先进行了参数的有效性检查,确保`php`不为空。然后,将`php`所指向的结构体中的`arr`成员设置为`NULL`,并将`size`和`capacity`成员都设置为`0`。 

1.2.4  堆的销毁
void HPDestroy(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}

代码逻辑: 

定义了一个 `HPDestroy` 的函数,用于销毁一个 `HP` 类型的指针 `php` 所指向的资源。函数内部首先进行参数有效性检查,确保 `php` 不为空。然后,判断 `php->arr` 是否不为空,如果不为空则使用 `free` 函数释放其占用的内存。最后,将 `php->arr` 设置为 `NULL`,并将 `php->size` 和 `php->capacity` 都设置为 `0`。 

1.2.5 向上调整算法 
//向上调整
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;parent = (child - 1) / 2;}else {break;}}
}

代码逻辑:

 这段 代码定义了一个`AdjustUp`的函数,用于对一个数组进行向上调整操作,以构建一个堆(这里的堆可能是大顶堆或小顶堆,具体取决于比较运算符的具体实现)。

以下是对代码的详细解释:

- `int parent = (child - 1) / 2;`:通过这个公式计算出当前节点`child`的父节点`parent`的索引。
- `while (child > 0)`:只要当前节点的索引大于 0,就会进入循环进行调整。
- `if (arr[child] > arr[parent])`:这里根据堆的性质进行判断,如果当前节点的值大于父节点的值(这里假设是构建大顶堆),则交换它们的位置。
- `Swap(&arr[child], &arr[parent])`:这是一个用于交换两个元素的函数调用。
- `child = parent; parent = (child - 1) / 2;`:交换位置后,更新当前节点和父节点的索引,以便继续向上调整。
- `else { break; }`:如果当前节点的值不大于父节点的值,那么就停止调整,退出循环。

总的来说,这个函数的作用是在堆的构建过程中,将一个可能不符合堆性质的子节点向上调整,以保持堆的性质。 

1.2.6 堆的插入
void HPPush(HP* 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, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//直接插入php->arr[php->size] = x;//向上调整AdjustUp(php->arr, php->size);++php->size;
}

 代码逻辑:

这段代码定义了一个名为`HPPush`的函数,用于向一个堆(`HP`)中插入一个元素。

以下是对代码的详细解释:

- `assert(php);`:用于检查指针`php`是否有效。
- `if (php->size == php->capacity)`:检查堆的当前大小是否等于其容量。如果是,说明需要进行扩容操作。
    - `int newCapacity = php->capacity == 0? 4 : 2 * php->capacity;`:根据当前容量的情况计算新的容量。如果当前容量为 0,则新容量为 4;否则,新容量为当前容量的 2 倍。
    - `HPDataType* tmp = (HPDataType*)realloc(php->arr, newCapacity * sizeof(HPDataType));`:使用`realloc`函数为堆的数组重新分配内存,以扩大其容量。
    - `if (tmp == NULL)`:如果`realloc`操作失败,会输出错误信息并退出程序。
    - 如果扩容成功,更新堆的数组指针和容量。
- `php->arr[php->size] = x;`:将新元素直接插入到堆的末尾。
- `AdjustUp(php->arr, php->size);`:调用`AdjustUp`函数对插入的元素进行向上调整,以保持堆的性质。
- `++php->size;`:增加堆的大小。

总的来说,这个函数的作用是在保证堆有足够空间的前提下,将一个元素插入到堆中,并通过向上调整来维持堆的结构。 

1.2.7 判空和向下调整算法
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//大堆: <//小堆: >if (child + 1 < n && arr[child] < arr[child + 1]){child++;}//大堆:>//小堆: <if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}

 代码逻辑:

 这段 代码中定义了两个函数:

- `bool HPEmpty(HP* php)`:这个函数用于检查一个堆(`HP`)是否为空。函数内部通过检查堆的大小是否为 0 来判断堆是否为空。如果堆的大小为 0,则返回`true`,表示堆为空;否则返回`false`,表示堆不为空。
- `void AdjustDown(HPDataType* arr, int parent, int n)`:这个函数用于对堆进行向下调整操作。函数通过比较父节点和子节点的值,将父节点与较大(或较小,具体取决于堆的类型)的子节点进行交换,以保持堆的性质。在函数中,首先找到左子节点的索引,然后通过比较左右子节点的值,找到较大(或较小)的子节点。接着,将父节点与较大(或较小)的子节点进行比较,如果子节点的值大于(或小于)父节点的值,则进行交换,并更新父节点和子节点的索引,继续进行下一轮的调整。如果子节点的值小于(或大于)父节点的值,则停止调整。

总的来说,这两个函数分别实现了检查堆是否为空和对堆进行向下调整的功能。  

  • 堆的删除 

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

 

  • 向下调整算法 
  •  将堆顶元素与堆中最后一个元素进行交换
  • 删除堆中最后一个元素
  • 将堆顶元素向下调整到满足堆特性为止

 

1.2.8 堆顶数据的删除和堆的删除
void HPPop(HP* php)
{assert(!HPEmpty(php));//0  php->size-1Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//向下调整AdjustDown(php->arr, 0, php->size);
}
HPDataType HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}
1.3 源代码(Heap.c) 

#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"void HPInit(HP* php)
{assert(php);php->arr = NULL;php->size = php->capacity = 0;
}
void HPDestroy(HP* php)
{assert(php);if (php->arr)free(php->arr);php->arr = NULL;php->size = php->capacity = 0;
}
void HPPrint(HP* php)
{for (int i = 0; i < php->size; i++){printf("%d ", php->arr[i]);}printf("\n");
}
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//向上调整
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;parent = (child - 1) / 2;}else {break;}}
}void HPPush(HP* 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, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}php->arr = tmp;php->capacity = newCapacity;}//直接插入php->arr[php->size] = x;//向上调整AdjustUp(php->arr, php->size);++php->size;
}
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}
//向下调整
void AdjustDown(HPDataType* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//大堆: <//小堆: >if (child + 1 < n && arr[child] < arr[child + 1]){child++;}//大堆:>//小堆: <if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}
void HPPop(HP* php)
{assert(!HPEmpty(php));//0  php->size-1Swap(&php->arr[0], &php->arr[php->size - 1]);--php->size;//向下调整AdjustDown(php->arr, 0, php->size);
}
HPDataType HPTop(HP* php)
{assert(!HPEmpty(php));return php->arr[0];
}
2.小结 

 以上便是关于堆的所有内容,如果大家喜欢这篇博客,还请给博主点点赞,谢谢大家!

相关文章:

  • zemax非序列棱镜面元理解
  • 4.18---缓存相关问题(操作原子性,击穿,穿透,雪崩,redis优势)
  • 电子电器架构 --- EOL 工厂刷写(产线)
  • 通信算法之267 : DJI无人机 云哨 DroneID 640ms
  • 【嵌入式八股11】STM32
  • 【无人机】电子速度控制器 (ESC) 驱动电机,常见的电调协议,PWM协议,Oneshot协议,DShot协议
  • 第十二讲、Isaaclab中使用RL对智能体进行训练
  • Let C语言通俗化
  • 深度解析商标显著性
  • 进程间通信(IPC)----共享内存
  • STM32单片机入门学习——第40节: [11-5] 硬件SPI读写W25Q64
  • OpenVINO怎么用
  • 限制外网用户WEB管理设备
  • npm i 安装遇到问题
  • 使用veaury,在vue项目中运行react组件
  • android的配置检查 查看安卓设备配置
  • 力扣面试150题--两数之和 和 快乐数
  • 第七篇:系统分析师第三遍——1、2章
  • 23种设计模式-创建型模式之抽象工厂模式(Java版本)
  • 从零到一:管理系统设计新手如何快速上手?
  • 冲线!“天工”夺得全球首个人形机器人半马冠军
  • 寒武纪一季度营收猛增42倍,净利3.55亿元,连续两个季度盈利
  • 福特预期6月美国市场涨价,机构称加税让每辆汽车成本至少增加数千美元
  • 皓元医药郑保富:共创、共赢、共享,跨域协作推动生物医药创新
  • 河北衡水中学再换校长
  • “80后”张汉强已任浙江丽水市委常委、市纪委书记