一、堆
堆是一种完全二叉树结构 ,通常使用数组 实现,分为大堆 和小堆
大堆:父节点 ≥ 子节点,堆顶为最大值
小堆:父节点 ≤ 子节点,堆顶为最小值
堆的核心操作依赖向上调整与向下调整 ,常用于堆排序 、TopK 问题 、优先级队列等
二、Heap.h文件
1. 堆结点的结构
c
// 支持灵活修改数据类型
typedef int HeapDataType;
// 堆结构体(标准实现)
typedef struct Heap
{
HeapDataType* array; // 动态数组
int size; // 有效元素个数
int capacity; // 容量
} HP;
2. 可以实现的函数接口
c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int HeapDataType;
typedef struct Heap
{
HeapDataType* array;
int size;
int capacity;
}HP;
// 交换两个元素
void swap(HeapDataType* x, HeapDataType* y);
// 默认初始化堆
void HPInit(HP* php);
// 判空
bool HPEmpty(HP* php);
// 求size
int HPSize(HP* php);
// 堆的打印
void HPPrint(HP* php);
// 扩容
void HPBuyArrayCapacity(HP* php);
// 堆的销毁
void HPDestroy(HP* php);
// 大堆向上调整算法
void MaxHPAdjustUp(HeapDataType* array, int child);
// 小堆向上调整算法
void MinHPAdjustUp(HeapDataType* array, int child);
// 大堆向下调整算法
void MaxHPAdjustDown(HeapDataType* array, int n, int parent);
// 小堆向下调整算法
void MinHPAdjustDown(HeapDataType* array, int n, int parent);
// 利用给定数组初始化为大堆
void MaxHPInitArray(HP* php, HeapDataType* array, int n);
// 利用给定数组初始化为小堆
void MinHPInitArray(HP* php, HeapDataType* array, int n);
// 大堆的插入
void MaxHPPush(HP* php, HeapDataType x);
// 小堆的插入
void MinHPPush(HP* php, HeapDataType x);
// 获取堆顶的数据
HeapDataType HPTop(HP* php);
// 删除大堆堆顶的数据
void MaxHPPop(HP* php);
// 删除小堆堆顶的数据
void MinHPPop(HP* php);
// 修改大堆中指定下标的值(并重新调整)
void MaxHPModify(HP* php, int pos, HeapDataType x);
// 修改小堆中指定下标的值(并重新调整)
void MinHPModify(HP* php, int pos, HeapDataType x);
// 堆排序(升序)
void HPSortAsc(HeapDataType* a, int n);
// 堆排序(降序)
void HPSortDesc(HeapDataType* array, int n);
三、Heap.c文件
1. swap 函数
swap 函数实现逻辑:
该函数用于交换两个 HeapDataType 类型变量的值,通过指针操作直接修改原变量内容,是堆调整过程中交换父子节点的基础工具函数
函数功能:交换两个 HeapDataType 类型变量的值
c
// 功能:交换两个HeapDataType类型变量的值
// 参数:x指向第一个待交换变量,y指向第二个待交换变量
void swap(HeapDataType* x, HeapDataType* y)
{
// 定义临时变量存储x指向空间的值
HeapDataType tmp = *x;
// 将y指向空间的值赋值给x指向的空间
*x = *y;
// 将临时变量中保存的原值赋值给y指向的空间
*y = tmp;
}
2. HPInit 函数
HPInit 函数实现逻辑:
该函数用于对堆结构体进行默认初始化 ,将结构体中的数组指针置空 、有效元素个数和容量都置为 0,为后续堆操作提供初始安全状态
函数功能:对堆结构体进行默认初始化
c
// 功能:对堆结构体进行默认初始化
// 参数:php指向待初始化的堆结构体
void HPInit(HP* php)
{
// 断言保证传入的堆结构体指针不为空,避免空指针访问
assert(php);
// 将堆的动态数组指针置空,初始不分配内存
php->array = NULL;
// 初始有效元素个数为0
php->size = 0;
// 初始数组容量为0
php->capacity = 0;
}
3. HPEmpty 函数
HPEmpty 函数实现逻辑:
该函数用于判断堆是否为空 ,通过检查堆中有效元素个数是否为 0 来返回bool结果,是后续获取堆顶、删除堆顶等操作的前置安全判断依据
函数功能:判断堆是否为空,为空返回 true,不为空返回 false
c
// 功能:判断堆是否为空,为空返回true,不为空返回false
// 参数:php指向待判断的堆结构体
// 返回值:为空返回true,非空返回false
bool HPEmpty(HP* php)
{
// 断言保证堆指针合法
assert(php);
// 有效元素个数为0则堆为空,返回判断结果
return php->size == 0;
}
4. HPSize 函数
HPSize 函数实现逻辑:
该函数用于获取堆中当前有效元素的个数,直接返回结构体中保存的 size 成员,为修改元素、遍历等操作提供边界依据
函数功能:返回堆中有效元素个数。
c
// 功能:返回堆中有效元素个数
// 参数:php指向堆结构体
// 返回值:堆当前有效元素数量
int HPSize(HP* php)
{
// 断言堆指针不为空
assert(php);
// 返回结构体中记录的有效元素个数
return php->size;
}
5. HPPrint 函数
HPPrint 函数实现逻辑:
该函数用于遍历并打印堆中所有有效元素,先判断堆是否为空,若不为空则从数组下标 0 开始逐个输出元素,用于调试观察堆结构
函数功能:打印堆中所有有效元素
c
// 功能:打印堆中所有有效元素
// 参数:php指向待打印的堆结构体
void HPPrint(HP* php)
{
// 断言堆指针合法
assert(php);
// 如果堆为空则直接返回,不执行打印
if (HPEmpty(php))
{
return;
}
// 遍历堆数组中所有有效元素
for (int i = 0; i < php->size; i++)
{
// 逐个打印元素并以空格分隔
printf("%d ", php->array[i]);
}
// 打印结束换行,使输出格式更整洁
printf("\n");
}
- HPBuyArrayCapacity 函数
HPBuyArrayCapacity函数实现逻辑:
该函数用于堆的动态扩容 ,当有效元素个数等于数组容量时进行扩容 ,初始容量为4,后续每次扩容为原来2倍 ,使用realloc分配内存并检查是否成功,保证插入操作不会越界
函数功能:检查堆的容量,不足则进行扩容。
c
// 功能:检查堆的容量,不足则进行扩容
// 参数:php指向需要扩容的堆结构体
void HPBuyArrayCapacity(HP* php)
{
// 断言堆指针不为空
assert(php);
// 判断当前有效元素是否已达到数组容量
if (php->size == php->capacity)
{
// 初始容量为0则设为4,否则扩容为原来2倍
int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
// 使用realloc重新分配更大的连续内存空间
HeapDataType* tmp = (HeapDataType*)realloc(php->array, sizeof(HeapDataType) * newcapacity);
// 判断内存分配是否成功
if (tmp == NULL)
{
// 打印系统级错误信息
perror("realloc failed");
// 内存分配失败直接退出程序
exit(-1);
}
// 将新分配的内存地址赋值给堆数组指针
php->array = tmp;
// 更新堆的容量为新容量
php->capacity = newcapacity;
}
}
7. HPDestroy 函数
HPDestroy 函数实现逻辑:
该函数用于销毁堆,释放堆中动态申请的数组内存,将数组指针置空 并重置 size 和 capacity 为 0,避免内存泄漏和野指针
函数功能:销毁堆,释放动态申请的内存。
c
// 功能:销毁堆,释放动态申请的内存
// 参数:php指向待销毁的堆结构体
void HPDestroy(HP* php)
{
// 断言堆指针合法
assert(php);
// 如果数组指针不为空则释放内存
if (php->array != NULL)
{
// 释放动态分配的数组空间
free(php->array);
}
// 将数组指针置空,防止野指针
php->array = NULL;
// 有效元素个数置0
php->size = 0;
// 容量置0
php->capacity = 0;
}
8. MaxHPAdjustUp 函数
MaxHPAdjustUp 函数实现逻辑:
该函数为大堆向上调整算法,从传入的子节点下标开始,不断与父节点比较,若子节点大于父节点则交换,直到满足大堆规则或到达根节点,用于插入元素后维护堆结构
函数功能:对大堆进行向上调整,维持大堆结构。
c
// 功能:对大堆进行向上调整,维持大堆结构
// 参数:array为堆数组,child为待调整的子节点下标
void MaxHPAdjustUp(HeapDataType* array, int child)
{
// 根据完全二叉树性质计算父节点下标
int parent = (child - 1) / 2;
// 循环向上调整,直到调整到根节点为止
while (child > 0)
{
// 大堆规则:子节点值大于父节点值需要交换
if (array[child] > array[parent])
{
// 交换子节点与父节点的值
swap(&array[child], &array[parent]);
// 将子节点下标更新为原父节点下标,继续向上调整
child = parent;
// 重新计算新的父节点下标
parent = (child - 1) / 2;
}
else
{
// 已满足大堆规则,无需继续调整,退出循环
break;
}
}
}
9. MinHPAdjustUp 函数
MinHPAdjustUp 函数实现逻辑:
该函数为小堆向上调整算法,从子节点开始向上比较,若子节点小于父节点则交换,直到满足小堆结构,用于小堆插入元素后调整
函数功能:对小堆进行向上调整,维持小堆结构。
c
// 功能:对小堆进行向上调整,维持小堆结构
// 参数:array为堆数组,child为待调整的子节点下标
void MinHPAdjustUp(HeapDataType* array, int child)
{
// 计算当前子节点对应的父节点下标
int parent = (child - 1) / 2;
// 循环向上调整直至根节点
while (child > 0)
{
// 小堆规则:子节点值小于父节点值需要交换
if (array[child] < array[parent])
{
// 交换父子节点值
swap(&array[child], &array[parent]);
// 子节点上移,继续调整
child = parent;
// 更新父节点下标
parent = (child - 1) / 2;
}
else
{
// 满足小堆规则,退出调整
break;
}
}
}
10. MaxHPAdjustDown 函数
MaxHPAdjustDown 函数实现逻辑:
该函数为大堆向下调整算法,从指定父节点开始,先选出左右孩子中较大的节点,若较大子节点大于父节点则交换,继续向下调整,直到满足大堆规则,用于删除堆顶与数组建堆
函数功能:对大堆进行向下调整,维持大堆结构。
c
// 功能:对大堆进行向下调整,维持大堆结构
// 参数:array为堆数组,n为有效元素个数,parent为待调整的父节点下标
void MaxHPAdjustDown(HeapDataType* array, int n, int parent)
{
// 计算当前父节点的左孩子下标
int child = 2 * parent + 1;
// 当孩子节点在有效范围内时继续调整
while (child < n)
{
// 若右孩子存在且更大,则将child指向右孩子
if ((child + 1 < n) && (array[child] < array[child + 1]))
{
child++;
}
// 若较大的孩子大于父节点则交换
if (array[child] > array[parent])
{
// 交换父节点与较大孩子节点
swap(&array[child], &array[parent]);
// 父节点下移,继续向下调整
parent = child;
// 重新计算新的左孩子下标
child = 2 * parent + 1;
}
else
{
// 满足大堆规则,直接返回
return;
}
}
}
11. MinHPAdjustDown 函数
MinHPAdjustDown 函数实现逻辑:
该函数为小堆向下调整算法,从父节点开始找到较小孩子,若其小于父节点则交换,持续向下调整以维护小堆结构,用于小堆删除堆顶与数组建小堆
函数功能:对小堆进行向下调整,维持小堆结构。
c
// 功能:对小堆进行向下调整,维持小堆结构
// 参数:array为堆数组,n为有效元素个数,parent为待调整父节点下标
void MinHPAdjustDown(HeapDataType* array, int n, int parent)
{
// 计算左孩子下标
int child = 2 * parent + 1;
// 孩子节点在有效范围内则循环
while (child < n)
{
// 若右孩子存在且更小,则child指向右孩子
if ((child + 1 < n) && (array[child] > array[child + 1]))
{
child++;
}
// 若较小孩子小于父节点则交换
if (array[child] < array[parent])
{
// 交换父子节点
swap(&array[child], &array[parent]);
// 父节点下移
parent = child;
// 重新计算孩子下标
child = 2 * parent + 1;
}
else
{
// 满足小堆规则,返回
return;
}
}
}
12. MaxHPInitArray 函数
MaxHPInitArray 函数实现逻辑:
该函数使用传入的数组初始化大堆,先分配内存并拷贝数据,再从最后一个非叶子节点开始倒序向下调整,使整个数组满足大堆规则
函数功能:利用给定数组初始化为大堆
c
// 功能:利用给定数组初始化为大堆
// 参数:php指向堆结构体,array为待建堆数组,n为数组长度
void MaxHPInitArray(HP* php, HeapDataType* array, int n)
{
// 断言堆指针不为空
assert(php);
// 断言数组不为空
assert(array);
// 断言数组长度合法
assert(n > 0);
// 为堆数组分配对应大小的内存
HeapDataType* tmp = (HeapDataType*)malloc(n * sizeof(HeapDataType));
// 判断内存分配是否成功
if (tmp == NULL)
{
perror("malloc failed");
exit(-1);
}
// 将分配的内存赋值给堆结构体数组指针
php->array = tmp;
// 有效元素个数为n
php->size = n;
// 容量为n
php->capacity = n;
// 将原数组内容拷贝到堆数组中
for (int i = 0; i < n; i++)
{
php->array[i] = array[i];
}
// 从最后一个非叶子节点开始向下调整建堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MaxHPAdjustDown(php->array, n, i);
}
}
13. MinHPInitArray 函数
MinHPInitArray 函数实现逻辑:
该函数通过数组初始化小堆,分配内存、拷贝数据后,从最后一个非叶子节点向前逐个向下调整,构建合法小堆
函数功能:利用给定数组初始化为小堆。
c
// 功能:利用给定数组初始化为小堆
// 参数:php指向堆结构体,array为待建堆数组,n为数组长度
void MinHPInitArray(HP* php, HeapDataType* array, int n)
{
assert(php); // 校验堆结构体指针
assert(array); // 校验源数组非空
assert(n > 0); // 校验数组长度合法
// 一次性开辟空间,避免多次扩容,效率极高
HeapDataType* tmp = (HeapDataType*)malloc(n * sizeof(HeapDataType));
if (tmp == NULL)
{
perror("malloc failed");
exit(-1);
}
php->array = tmp;
php->size = n; // 有效元素个数 = 数组长度
php->capacity = n; // 容量 = 数组长度
// 拷贝数组数据
for (int i = 0; i < n; i++)
{
php->array[i] = array[i];
}
// 核心:从最后一个非叶子节点开始,向下调整建小堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MinHPAdjustDown(php->array, n, i);
}
}
14. MaxHPPush 函数
MaxHPPush 函数实现逻辑:
该函数用于大堆插入元素,先扩容,再将元素插入数组末尾,最后对末尾元素向上调整以维持大堆结构 ,size 自增
函数功能:向大堆中插入一个元素。
c
// 功能:向大堆中插入一个元素
// 参数:php指向堆结构体,x为待插入元素值
void MaxHPPush(HP* php, HeapDataType x)
{
// 断言堆指针合法
assert(php);
// 插入前检查并扩容
HPBuyArrayCapacity(php);
// 将新元素放入数组末尾
php->array[php->size] = x;
// 对末尾元素进行向上调整维持大堆
MaxHPAdjustUp(php->array, php->size);
// 有效元素个数加1
++php->size;
}
15. MinHPPush 函数
MinHPPush 函数实现逻辑:
该函数向小堆插入元素,先扩容,插入末尾后向上调整,保证小堆性质
函数功能:向小堆中插入一个元素。
c
// 功能:向小堆中插入一个元素
// 参数:php指向堆结构体,x为待插入值
void MinHPPush(HP* php, HeapDataType x)
{
assert(php); // 校验指针非空
// 先检查扩容(满了才扩)
HPBuyArrayCapacity(php);
// 新元素放到数组末尾
php->array[php->size] = x;
// 小堆向上调整
MinHPAdjustUp(php->array, php->size);
// 元素个数+1
++php->size;
}
16. HPTop 函数
HPTop 函数实现逻辑:
该函数用于获取堆顶元素,堆顶对应数组下标 0,使用前会判断堆非空,保证访问安全
函数功能:获取堆顶的数据
c
// 功能:获取堆顶的数据
// 参数:php指向堆结构体
// 返回值:堆顶元素值
HeapDataType HPTop(HP* php)
{
// 断言堆不为空
assert(!HPEmpty(php));
// 堆顶对应数组第一个元素
return php->array[0];
}
17. MaxHPPop 函数
MaxHPPop 函数实现逻辑:
该函数删除大堆堆顶,先交换堆顶与末尾元素 ,size 减 1,再对新堆顶向下调整,保证删除后仍为大堆
函数功能:删除大堆堆顶的数据
c
// 功能:删除大堆堆顶的数据
// 参数:php指向堆结构体
void MaxHPPop(HP* php)
{
assert(php);
assert(!HPEmpty(php));
// 交换堆顶与最后一个元素
swap(&php->array[0], &php->array[php->size - 1]);
// 有效元素个数减1,逻辑删除原堆顶
--php->size;
// 对新堆顶向下调整恢复大堆
MaxHPAdjustDown(php->array, php->size, 0);
}
18. MinHPPop 函数
MinHPPop 函数实现逻辑:
该函数删除小堆堆顶,交换堆顶与末尾元素后逻辑删除,再向下调整维持小堆
函数功能:删除小堆堆顶的数据。
c
// 删除小堆堆顶元素
void MinHPPop(HP* php)
{
assert(php); // 校验结构体指针合法
assert(!HPEmpty(php)); // 空堆禁止删除
// 1. 堆顶 和 最后一个元素交换
swap(&php->array[0], &php->array[php->size - 1]);
// 2. 有效元素个数 -1(删除原堆顶)
--php->size;
// 3. 从根节点向下调整,维护小堆结构
MinHPAdjustDown(php->array, php->size, 0);
}
19. MaxHPModify 函数
MaxHPModify 函数实现逻辑:
该函数修改大堆指定下标值,比较新旧值判断向上或向下调整,保证修改后仍满足大堆结构
函数功能:修改大堆中指定下标的值并重新调整
c
// 功能:修改大堆中指定下标位置的元素值,并自动调整结构,保证仍然是大堆
// 参数:php -> 指向堆结构体的指针(操作哪个堆)
// pos -> 要修改的元素在数组中的下标
// x -> 要修改成的新值
void MaxHPModify(HP* php, int pos, HeapDataType x)
{
// 1. 检查堆结构体指针不为空
assert(php);
// 2. 检查堆不是空的,空堆不能修改
assert(!HPEmpty(php));
// 3. 检查下标是否合法(必须 >= 0 且小于当前元素个数),防止越界
assert(pos >= 0 && pos < HPSize(php));
// 保存修改前的旧值,用于后续判断向上/向下调整
HeapDataType old = php->array[pos];
// 将指定下标的元素直接修改为新值
php->array[pos] = x;
// 情况1:新值 **比旧值小**
// 说明这个节点变小了,可能比孩子还小 → 破坏大堆 → 需要向下沉
if (x < old)
{
// 从当前位置开始向下调整,恢复大堆结构
MaxHPAdjustDown(php->array, php->size, pos);
}
// 情况2:新值 **比旧值大**
// 说明这个节点变大了,可能比父亲还大 → 破坏大堆 → 需要向上浮
else if (x > old)
{
// 从当前位置开始向上调整,恢复大堆结构
MaxHPAdjustUp(php->array, pos);
}
// 情况3:x == old
// 值没有变化 → 堆结构不受影响 → 什么都不用做
}
20. MinHPModify 函数
MinHPModify 函数实现逻辑:
该函数修改小堆指定位置值,根据大小关系选择向上或向下调整,维持小堆结构
函数功能:修改小堆中指定下标的值并重新调整
c
// 功能:修改小堆中指定下标位置的元素值,并自动调整堆结构,保证仍然是小堆
// 参数:php -> 指向小堆结构体的指针
// pos -> 要修改的元素在数组中的下标位置
// x -> 修改后的新值
void MinHPModify(HP* php, int pos, HeapDataType x)
{
// 断言1:确保堆结构体指针不为空,防止空指针访问崩溃
assert(php);
// 断言2:确保堆不为空,空堆不能修改元素
assert(!HPEmpty(php));
// 断言3:确保下标合法,范围在 [0, 元素个数-1] 之间,防止数组越界
assert(pos >= 0 && pos < HPSize(php));
// 保存修改前的旧值,用于和新值比较,判断调整方向
HeapDataType old = php->array[pos];
// 直接将指定下标的值修改为新值
php->array[pos] = x;
// 情况1:新值 > 旧值
// 说明该节点变大了,可能比孩子节点大,破坏了小堆结构
// 因此需要向下调整,让它往下沉到合适位置
if (x > old)
{
MinHPAdjustDown(php->array, php->size, pos);
}
// 情况2:新值 < 旧值
// 说明该节点变小了,可能比父节点更小,破坏了小堆结构
// 因此需要向上调整,让它往上浮到合适位置
else if (x < old)
{
MinHPAdjustUp(php->array, pos);
}
// 情况3:新值 == 旧值
// 堆结构没有任何变化,不需要做任何调整
}
21. HPSortAsc 函数
HPSortAsc函数实现逻辑:
堆排序升序使用大堆 ,先将数组构建为大堆,再不断将堆顶与未排序区间末尾交换,然后调整剩余元素,最终数组升序
函数功能:对数组进行堆排序,排序结果为升序
c
// 功能:对数组进行堆排序,排序结果为升序(从小到大)
// 核心原理:升序排序 → 建大堆
// 大堆堆顶是最大值,每次把最大值放到末尾,最终数组从小到大
// 参数:a 待排序的数组
// n 数组元素个数
void HPSortAsc(HeapDataType* a, int n)
{
// 把数组调整成大堆
// 从最后一个非叶子节点开始,从下往上、从右往左调整
// 最后一个非叶子节点下标公式:(最后一个元素下标 - 1) / 2 → (n-1-1)/2
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
// 调用大堆向下调整,将数组构建成大堆
MaxHPAdjustDown(a, n, i);
}
// 依次删除堆顶
// end 标记【未排序区间】的最后一个元素位置
int end = n - 1;
// 未排序区间只剩一个元素时,已经有序,退出循环
while (end > 0)
{
// 1. 交换堆顶(最大值)和未排序区间最后一个元素
// 最大值直接放到它最终的位置(固定下来)
swap(&a[0], &a[end]);
// 2. 对前面 0 ~ end-1 的未排序区间,重新调整为大堆
// 区间长度变为 end,从堆顶(下标0)开始向下调整
MaxHPAdjustDown(a, end, 0);
// 3. 未排序区间长度 -1
end--;
}
}
22. HPSortDesc 函数
HPSortDesc 函数实现逻辑:
堆排序降序使用小堆,先构建小堆,再不断交换堆顶与末尾,调整后最终数组降序
函数功能:对数组进行堆排序,排序结果为降序
c
// 功能:对数组进行堆排序,排序结果为【降序】(从大到小)
// 原理:降序排序 → 建小堆
// 小堆堆顶是最小值,把最小值依次交换到数组末尾,最终实现从大到小
// 参数:array 待排序数组
// n 数组长度
void HPSortDesc(HeapDataType* array, int n)
{
// 建小堆
// 从最后一个非叶子节点开始,向前遍历,依次向下调整
// 最后一个非叶子节点下标:(n - 1 - 1) / 2 → 即 (最后一个元素下标 - 1) / 2
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
// 调用小堆向下调整,把数组调整为小堆结构
MinHPAdjustDown(array, n, i);
}
// 依次删除堆顶
// end 指向当前未排序区间的最后一个位置
int end = n - 1;
// 当未排序区间只剩一个元素时,已经有序,停止循环
while (end > 0)
{
// 1. 交换:将堆顶(最小值)与未排序区间最后一个元素交换
// 最小值放到未排序区间末尾,固定下来
swap(&array[0], &array[end]);
// 2. 调整:对前面未排序区间(0 ~ end-1)重新调整为小堆
// 区间长度变成 end,堆顶位置 0 开始向下调整
MinHPAdjustDown(array, end, 0);
// 3. 未排序区间长度 -1
end--;
}
}
四、所有代码
1.Heap.h
c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int HeapDataType;
typedef struct Heap
{
HeapDataType* array;
int size;
int capacity;
}HP;
// 交换两个元素
void swap(HeapDataType* x, HeapDataType* y);
// 默认初始化堆
void HPInit(HP* php);
// 判空
bool HPEmpty(HP* php);
// 求sizeh
int HPSize(HP* php);
// 堆的打印
void HPPrint(HP* php);
// 扩容
void HPBuyArrayCapacity(HP* php);
// 堆的销毁
void HPDestroy(HP* php);
// 大堆向上调整算法
void MaxHPAdjustUp(HeapDataType* array, int child);
// 小堆向上调整算法
void MinHPAdjustUp(HeapDataType* array, int child);
// 大堆向下调整算法
void MaxHPAdjustDown(HeapDataType* array, int n, int parent);
// 小堆向下调整算法
void MinHPAdjustDown(HeapDataType* array, int n, int parent);
// 利用给定数组初始化为大堆
void MaxHPInitArray(HP* php, HeapDataType* array, int n);
// 利用给定数组初始化为小堆
void MinHPInitArray(HP* php, HeapDataType* array, int n);
// 大堆的插入
void MaxHPPush(HP* php, HeapDataType x);
// 小堆的插入
void MinHPPush(HP* php, HeapDataType x);
// 获取堆顶的数据
HeapDataType HPTop(HP* php);
// 删除大堆堆顶的数据
void MaxHPPop(HP* php);
// 删除小堆堆顶的数据
void MinHPPop(HP* php);
// 修改大堆中指定下标的值(并重新调整)
void MaxHPModify(HP* php, int pos, HeapDataType x);
// 修改小堆中指定下标的值(并重新调整)
void MinHPModify(HP* php, int pos, HeapDataType x);
// 堆排序(升序)
void HPSortAsc(HeapDataType* a, int n);
// 堆排序(降序)
void HPSortDesc(HeapDataType* array, int n);
2.Heap.c
c
// 功能:检查堆容量,空间不足时扩容
// 参数:php指向堆结构体
void HPBuyArrayCapacity(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 当有效元素个数等于数组容量时,说明空间已满需要扩容
if (php->size == php->capacity)
{
// 新容量:如果容量为0则初始化为4,否则扩容为原来的2倍
int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
// 使用realloc调整内存大小
HeapDataType* tmp = (HeapDataType*)realloc(php->array, sizeof(HeapDataType) * newcapacity);
// 判断内存开辟是否失败
if (tmp == NULL)
{
// 打印错误信息
perror(" realloc failed");
// 退出程序
exit(-1);
}
// 将新开辟的空间地址赋值给堆的数组指针
php->array = tmp;
// 更新容量为新容量
php->capacity = newcapacity;
}
}
// 功能:堆的默认初始化
// 参数:php指向堆结构体
void HPInit(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 初始化数组指针为空
php->array = NULL;
// 初始有效元素个数为0
php->size = 0;
// 初始容量为0
php->capacity = 0;
}
// 功能:判断堆是否为空
// 参数:php指向堆结构体
// 返回值:为空返回true,不为空返回false
bool HPEmpty(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 有效元素个数为0表示堆为空
return php->size == 0;
}
// 功能:销毁堆,释放动态内存
// 参数:php指向堆结构体
void HPDestroy(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 如果数组指针不为空,释放动态开辟的内存
if (php->array != NULL)
{
free(php->array);
}
// 将数组指针置空,避免野指针
php->array = NULL;
// 有效元素个数置0
php->size = 0;
// 容量置0
php->capacity = 0;
}
// 功能:打印堆中所有有效元素
// 参数:php指向堆结构体
void HPPrint(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 如果堆为空,直接返回不打印
if (HPEmpty(php))
{
return;
}
// 遍历所有有效元素并打印
for (size_t i = 0; i < php->size; i++)
{
printf("%d ", php->array[i]);
}
// 打印完换行
printf("\n");
}
// 功能:交换两个整型变量的值
// 参数:x、y为待交换的两个变量地址
void swap(HeapDataType* x, HeapDataType* y)
{
// 定义临时变量存储x指向的值
HeapDataType tmp = *x;
// 将y的值赋给x
*x = *y;
// 将临时变量的值赋给y
*y = tmp;
}
// 功能:大堆向上调整算法,保持大堆结构
// 参数:array待调整数组,child待调整节点的下标
void MaxHPAdjustUp(HeapDataType* array, int child)
{
// 计算当前节点的父节点下标
int parent = (child - 1) / 2;
// 子节点下标大于0时进入循环(根节点无父节点)
while (child > 0)
{
// 如果子节点值大于父节点值,不符合大堆规则
if (array[child] > array[parent])
{
// 交换子节点和父节点的值
swap(&array[child], &array[parent]);
// 更新子节点为原父节点下标
child = parent;
// 重新计算新的父节点下标
parent = (child - 1) / 2;
}
else
{
// 符合大堆规则,直接退出循环
break;
}
}
}
// 功能:小堆向上调整算法,保持小堆结构
// 参数:array待调整数组,child待调整节点的下标
void MinHPAdjustUp(HeapDataType* array, int child)
{
// 计算当前节点的父节点下标
int parent = (child - 1) / 2;
// 子节点下标大于0时进入循环
while (child > 0)
{
// 如果子节点值小于父节点值,不符合小堆规则
if (array[child] < array[parent])
{
// 交换子节点和父节点的值
swap(&array[child], &array[parent]);
// 更新子节点为原父节点下标
child = parent;
// 重新计算新的父节点下标
parent = (child - 1) / 2;
}
else
{
// 符合小堆规则,退出循环
break;
}
}
}
// 功能:向大堆中插入元素
// 参数:php指向堆结构体,x待插入元素
void MaxHPPush(HP* php, HeapDataType x)
{
// 确保堆结构体指针不为空
assert(php);
// 检查并扩容,保证有空间插入
HPBuyArrayCapacity(php);
// 将新元素放到数组末尾
php->array[php->size] = x;
// 对新元素进行向上调整,维持大堆结构
MaxHPAdjustUp(php->array, php->size);
// 有效元素个数加1
++php->size;
}
// 功能:向小堆中插入元素
// 参数:php指向堆结构体,x待插入元素
void MinHPPush(HP* php, HeapDataType x)
{
// 确保堆结构体指针不为空
assert(php);
// 检查并扩容,保证有空间插入
HPBuyArrayCapacity(php);
// 将新元素放到数组末尾
php->array[php->size] = x;
// 对新元素进行向上调整,维持小堆结构
MinHPAdjustUp(php->array, php->size);
// 有效元素个数加1
++php->size;
}
// 功能:获取堆顶元素
// 参数:php指向堆结构体
// 返回值:堆顶元素值
HeapDataType HPTop(HP* php)
{
// 确保堆不为空
assert(!HPEmpty(php));
// 堆顶元素存储在数组0号下标位置
return php->array[0];
}
// 功能:小堆向下调整算法,保持小堆结构
// 参数:array待调整数组,n数组元素个数,parent待调整节点下标
void MinHPAdjustDown(HeapDataType* array, int n, int parent)
{
// 计算左孩子节点下标
int child = 2 * parent + 1;
// 孩子节点下标小于元素个数时进入循环
while (child < n)
{
// 找到左右孩子中较小的节点
// 如果右孩子存在且右孩子更小
if ((child + 1 < n) && (array[child] > array[child + 1]))
{
// 切换到右孩子
child++;
}
// 如果较小的孩子比父节点小,不符合小堆规则
if (array[child] < array[parent])
{
// 交换孩子与父节点
swap(&array[child], &array[parent]);
// 更新父节点为孩子节点下标
parent = child;
// 重新计算孩子节点下标
child = 2 * parent + 1;
}
else
{
// 符合小堆规则,退出函数
return;
}
}
}
// 功能:删除小堆堆顶元素
// 参数:php指向小堆结构体
void MinHPPop(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 确保堆不为空
assert(!HPEmpty(php));
// 交换堆顶与最后一个元素
swap(&php->array[0], &php->array[php->size - 1]);
// 有效元素个数减1,逻辑删除原堆顶元素
--php->size;
// 从根节点开始向下调整,维持小堆结构
MinHPAdjustDown(php->array, php->size, 0);
}
// 功能:大堆向下调整算法,保持大堆结构
// 参数:array待调整数组,n数组元素个数,parent待调整节点下标
void MaxHPAdjustDown(HeapDataType* array, int n, int parent)
{
// 计算左孩子节点下标
int child = 2 * parent + 1;
// 孩子节点下标小于元素个数时进入循环
while (child < n)
{
// 找到左右孩子中较大的节点
// 如果右孩子存在且右孩子更大
if ((child + 1 < n) && (array[child] < array[child + 1]))
{
// 切换到右孩子
child++;
}
// 如果较大的孩子比父节点大,不符合大堆规则
if (array[child] > array[parent])
{
// 交换孩子与父节点
swap(&array[child], &array[parent]);
// 更新父节点为孩子节点下标
parent = child;
// 重新计算孩子节点下标
child = 2 * parent + 1;
}
else
{
// 符合大堆规则,退出函数
return;
}
}
}
// 功能:删除大堆堆顶元素
// 参数:php指向大堆结构体
void MaxHPPop(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 确保堆不为空
assert(!HPEmpty(php));
// 交换堆顶与最后一个元素
swap(&php->array[0], &php->array[php->size - 1]);
// 有效元素个数减1,逻辑删除原堆顶元素
--php->size;
// 从根节点开始向下调整,维持大堆结构
MaxHPAdjustDown(php->array, php->size, 0);
}
// 功能:获取堆中有效元素个数
// 参数:php指向堆结构体
// 返回值:有效元素个数
int HPSize(HP* php)
{
// 确保堆结构体指针不为空
assert(php);
// 返回有效元素个数
return php->size;
}
// 功能:利用数组初始化大堆
// 参数:php指向堆结构体,array待建堆数组,n数组长度
void MaxHPInitArray(HP* php, HeapDataType* array, int n)
{
// 确保堆结构体指针不为空
assert(php);
// 确保数组指针不为空
assert(array);
// 确保数组长度大于0
assert(n > 0);
// 动态开辟内存存储数组元素
HeapDataType* tmp = (HeapDataType*)malloc(n * sizeof(HeapDataType));
// 判断内存开辟是否失败
if (tmp == NULL)
{
// 打印错误信息
perror("malloc failed");
// 退出程序
exit(-1);
}
// 将开辟的空间赋值给堆数组指针
php->array = tmp;
// 有效元素个数为数组长度
php->size = n;
// 容量为数组长度
php->capacity = n;
// 将原数组元素拷贝到堆数组中
for (int i = 0; i < n; i++)
{
php->array[i] = array[i];
}
// 从最后一个非叶子节点开始向下调整,构建大堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MaxHPAdjustDown(php->array, n, i);
}
}
// 功能:利用数组初始化小堆
// 参数:php指向堆结构体,array待建堆数组,n数组长度
void MinHPInitArray(HP* php, HeapDataType* array, int n)
{
// 确保堆结构体指针不为空
assert(php);
// 确保数组指针不为空
assert(array);
// 确保数组长度大于0
assert(n > 0);
// 动态开辟内存存储数组元素
HeapDataType* tmp = (HeapDataType*)malloc(n * sizeof(HeapDataType));
// 判断内存开辟是否失败
if (tmp == NULL)
{
// 打印错误信息
perror("malloc failed");
// 退出程序
exit(-1);
}
// 将开辟的空间赋值给堆数组指针
php->array = tmp;
// 有效元素个数为数组长度
php->size = n;
// 容量为数组长度
php->capacity = n;
// 将原数组元素拷贝到堆数组中
for (int i = 0; i < n; i++)
{
php->array[i] = array[i];
}
// 从最后一个非叶子节点开始向下调整,构建小堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MinHPAdjustDown(php->array, n, i);
}
}
// 功能:堆排序(升序)
// 参数:array待排序数组,n数组长度
void HPSortAsc(HeapDataType* array, int n)
{
// 构建大堆,升序排序使用大堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MaxHPAdjustDown(array, n, i);
}
// end指向未排序区间最后一个元素
int end = n - 1;
// 未排序区间元素大于1个时循环
while (end > 0)
{
// 交换堆顶最大值与未排序区间最后一个元素
swap(&array[0], &array[end]);
// 对前面未排序区间调整为大堆
MaxHPAdjustDown(array, end, 0);
// 未排序区间长度减1
end--;
}
}
// 功能:堆排序(降序)
// 参数:array待排序数组,n数组长度
void HPSortDesc(HeapDataType* array, int n)
{
// 构建小堆,降序排序使用小堆
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
MinHPAdjustDown(array, n, i);
}
// end指向未排序区间最后一个元素
int end = n - 1;
// 未排序区间元素大于1个时循环
while (end > 0)
{
// 交换堆顶最小值与未排序区间最后一个元素
swap(&array[0], &array[end]);
// 对前面未排序区间调整为小堆
MinHPAdjustDown(array, end, 0);
// 未排序区间长度减1
end--;
}
}
// 功能:修改大堆指定下标元素并调整
// 参数:php指向堆,pos下标,x新值
void MaxHPModify(HP* php, int pos, HeapDataType x)
{
// 确保堆结构体指针不为空
assert(php);
// 确保堆不为空
assert(!HPEmpty(php));
// 确保下标合法
assert(pos >= 0 && pos < HPSize(php));
// 保存修改前的旧值
HeapDataType old = php->array[pos];
// 将指定下标的值修改为新值
php->array[pos] = x;
// 新值比旧值小,节点可能需要下沉,向下调整
if (x < old)
{
MaxHPAdjustDown(php->array, php->size, pos);
}
// 新值比旧值大,节点可能需要上浮,向上调整
if (x > old)
{
MaxHPAdjustUp(php->array, pos);
}
}
// 功能:修改小堆指定下标元素并调整
// 参数:php指向堆,pos下标,x新值
void MinHPModify(HP* php, int pos, HeapDataType x)
{
// 确保堆结构体指针不为空
assert(php);
// 确保堆不为空
assert(!HPEmpty(php));
// 确保下标合法
assert(pos >= 0 && pos < HPSize(php));
// 保存修改前的旧值
HeapDataType old = php->array[pos];
// 将指定下标的值修改为新值
php->array[pos] = x;
// 新值比旧值大,节点可能需要下沉,向下调整
if (x > old)
{
MinHPAdjustDown(php->array, php->size, pos);
}
// 新值比旧值小,节点可能需要上浮,向上调整
if (x < old)
{
MinHPAdjustUp(php->array, pos);
}
}