目录
这段代码主要定义了一个堆的数据结构和相关操作。堆是一种特殊的树形数据结构,它满足堆的性质要求,通常被用于实现优先队列等数据结构。堆可以作为最大堆或最小堆来使用,这取决于如何对数据进行初始化。
主要函数包括: * `HeapInit(Heap* hp)`:初始化堆的数据结构,包括设置堆的元素指针数组、当前元素个数、最大容量为NULL和0。
* `HeapDestroy(Heap* hp)`:销毁堆的数据结构,释放堆中的内存空间并设置为NULL。 * `HeapPrint(Heap* hp)`:打印堆中的元素并换行。
* `Swap(HPDataType* _a, int child, int parent)`:交换两个元素的值。
* `AdjustUp(HPDataType* _a, int child)`:向上调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)。
* `HeapPush(Heap* hp, HPDataType x)`:插入新的元素到堆中,并调整堆的性质以满足堆的要求。
* `AdjustDown(Heap* hp)`:向下调整函数,调整堆的性质以满足堆的性质要求(最大堆或最小堆)。
* `HeapPop(Heap* hp)`:删除堆顶元素并调整堆的性质以满足堆的要求。
* `HeapTop(Heap* hp)`:返回堆中的第一个元素的值。
* `HeapSize(Heap* hp)`:返回堆中的元素个数。
* `HeapEmpty(Heap* hp)`:判断堆是否为空。
这段代码还包含了一些断言(assert)语句,用于在代码执行过程中检查某些条件是否为真,如果条件不满足,程序会抛出异常并退出。这对于调试代码非常有用,可以帮助开发者快速定位问题所在。
// 头文件包含
#include<stdio.h> // 输入输出库,用于输入输出操作
#include<stdlib.h> // 常用的一些函数库,例如malloc, realloc等
#include<assert.h> // 断言库,用于调试代码
#include<stdbool.h> // 定义bool类型和相关的函数
// 定义堆的数据类型和结构体
typedef int HPDataType; // 将int类型命名为HPDataType,用于表示堆中的数据类型
typedef struct Heap // 定义一个结构体Heap,用于表示堆的数据结构
{
HPDataType* _a; // 指向堆中元素的指针数组
int _size; // 当前堆中元素的个数
int _capacity; // 堆的最大容量
}Heap; // 结构体名称为Heap
// 堆的初始化函数,将堆的元素指针数组、当前元素个数、最大容量都初始化为NULL和0
void HeapInit(Heap* hp)
{
hp->_a = NULL;
hp->_capacity = 0;
hp->_size = 0;
}
// 堆的销毁函数,释放堆中的内存空间,并将元素指针数组、当前元素个数、最大容量都设置为NULL和0
void HeapDestory(Heap* hp)
{
assert(hp); // 断言检查堆是否为空,如果为空则调用free释放内存并设置指针为NULL
free(hp->_a); // 释放内存空间
hp->_a = NULL; // 将指针设置为NULL
hp->_capacity = 0; // 将最大容量设置为0
hp->_size = 0; // 将当前元素个数设置为0
}
// 堆的打印函数,输出堆中的元素并换行
void HeapPrint(Heap* hp)
{
assert(hp); // 断言检查堆是否为空,如果为空则直接返回
int i = 0; // 循环变量,遍历所有元素并输出
for (i = 0; i < hp->_size; i++) // 遍历所有元素并输出到控制台
{
printf("%3d", hp->_a[i]); // 使用printf输出元素的值,使用%3d控制输出宽度为3个字符并左对齐
}
printf("\n"); // 换行符,输出完所有元素后换行
}
// 交换函数,交换两个元素的值
void Swap(HPDataType* _a, int child, int parent)
{
assert(_a); // 断言检查_a是否为空指针,如果为空则直接返回或抛出异常
int tmp = 0; // 临时变量用于交换两个元素的值
tmp = _a[child]; // 将child位置的元素值赋给临时变量tmp
_a[child] = _a[parent]; // 将parent位置的元素值赋给child位置的原值(相当于将parent位置的值交换到了child位置)
_a[parent] = tmp; // 将临时变量tmp的值赋给parent位置的新值(相当于将原来的child位置的值换为了parent位置的新值)
}
// 向上调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)
void AdjustUp(HPDataType* _a, int child) // child为需要调整的元素的索引位置(下标从0开始)
{
assert(_a); // 断言检查_a是否为空指针,如果为空则直接返回或抛出异常
int parent = 0; // 上一个父节点索引位置(下标从0开始)
while (child > 0) // 当child大于等于0时循环执行以下操作(即当前节点不是根节点)
{
parent = (child - 1) / 2; // 根据二叉树的上序遍历规则计算出上一个父节点的索引位置(注意是除以2而不是减1)
if (_a[parent] > _a[child]) // 如果上一个父节点的值大于当前节点的值(说明不符合堆的性质要求)
{
Swap(_a, child, parent); // 则交换当前节点和上一个父节点的值(即调整当前节点的值到符合要求的位置)并继续向下调整或者循环结束(即完成了一次向上调整)
child = parent; // 将当前节点设为需要调整的节点,重新开始向上遍历下一层节点
}
else
{
break;
}
}
}
// 堆的插入函数,将新的元素插入到堆中
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
if (hp->_capacity == hp->_size) // 如果堆的当前元素个数等于最大容量(堆已满)
{
int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2; // 将新的容量设置为原最大容量的两倍(如果原最大容量为0,则设置为4)
HPDataType* newheap = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity); // 使用realloc函数重新分配内存空间,将堆的元素指针数组大小调整为新的容量大小
if (newheap == NULL) // 检查内存分配是否成功
{
perror("realloc"); // 输出错误信息
exit(-1); // 程序异常退出
}
hp->_a = newheap; // 将新分配的内存空间赋给堆的元素指针数组
hp->_capacity = newcapacity; // 将堆的最大容量更新为新的容量大小
}
hp->_a[hp->_size] = x; // 在堆的末尾插入新的元素
AdjustUp(hp->_a, hp->_size); // 调整堆的性质以满足堆的要求
hp->_size++; // 堆的元素个数自增
}
?
// 向下调整函数,调整堆的性质以保证满足堆的性质要求(最大堆或最小堆)
void AdjustDown(Heap* hp)
{
int parent = 0; // 当前父节点的索引位置(下标从0开始)
int child = 0; // 当前子节点的索引位置
while (parent * 2 + 1 < hp->_size) // 当父节点的左子节点存在时循环执行以下操作
{
child = parent * 2 + 1; // 根据二叉树的左子节点索引位置计算出子节点的索引位置
if (child + 1 < hp->_size && hp->_a[child] > hp->_a[child + 1]) // 如果子节点的右子节点存在且右子节点的值小于左子节点的值
{
child = child + 1; // 则将子节点的索引位置设置为右子节点的索引位置(即选择右子节点作为下一个比较的节点)
}
if (hp->_a[child] < hp->_a[parent]) // 如果子节点的值小于父节点的值(说明不符合堆的性质要求)
{
Swap(hp->_a, child, parent); // 则交换子节点和父节点的值(即调整子节点的值到符合要求的位置)并继续向下调整或者循环结束(即完成了一次向下调整)
parent = child; // 将子节点设为父节点,重新开始向下遍历下一层节点
}
else
{
break; // 如果符合堆的性质要求,则跳出循环
}
}
}
// 堆的删除函数,删除堆顶元素(最大堆中的最大值或最小堆中的最小值)
void HeapPop(Heap* hp)
{
assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
Swap(hp->_a, hp->_size - 1, 0); // 将堆顶元素和最后一个元素交换位置
AdjustDown(hp); // 调整堆的性质以满足堆的要求
hp->_size--; // 堆的元素个数自减
}
// 取堆顶的数据,即返回堆中的第一个元素的值
HPDataType HeapTop(Heap* hp)
{
assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
return hp->_a[0]; // 返回堆的第一个元素的值
}
/ 堆的数据个数,即返回堆中的元素个数
int HeapSize(Heap* hp)
{
assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
return hp->_size; // 返回堆的元素个数
}
// 堆的判空,判断堆是否为空
bool HeapEmpty(Heap* hp)
{
assert(hp); // 断言检查hp是否为空指针,如果为空则直接返回或抛出异常
assert(hp->_a); // 断言检查堆的元素指针数组是否为空指针,如果为空则直接返回或抛出异常
if (hp->_size == 0) // 如果堆的元素个数为0
{
return false; // 则堆为空,返回false
}
else
{
return true; // 否则堆不为空,返回true
}
}