7.4 KiB
介绍
堆(Heap)是一种特殊的完全二叉树,具有特定的堆性质。堆主要分为两种类型:最大堆和最小堆。
-
概念:
- 堆是一种完全二叉树,除了最后一层外,每一层都是满的,并且最后一层的节点尽可能地向左排列。
- 堆中每个节点的值都满足堆性质,即在最大堆中,每个节点的值大于或等于其子节点的值;在最小堆中,每个节点的值小于或等于其子节点的值[^1^][^2^][^3^][^4^][^5^]。
-
结构:
- 堆通常通过数组来实现,因为堆是一棵完全二叉树,可以高效地映射到数组中。
- 假设堆的根节点存储在数组的第一个位置(索引为0),那么对于任意一个节点,其子节点和父节点的位置可以通过以下公式计算:
- 父节点索引:
parent(i) = (i - 1) / 2 - 左子节点索引:
left(i) = 2i + 1 - 右子节点索引:
right(i) = 2i + 2[^2^][^4^]
- 父节点索引:
-
完全二叉树:堆总是一棵完全二叉树,即除了最后一层外,所有层都是满的,且最后一层的节点尽可能地向左排列[^1^][^2^][^3^][^4^][^5^]。
-
堆序性质:
- 最大堆:每个节点的值大于或等于其子节点的值,这保证了堆顶(根节点)是整个堆中的最大值[^1^][^2^][^3^][^4^][^5^]。
- 最小堆:每个节点的值小于或等于其子节点的值,这保证了堆顶(根节点)是整个堆中的最小值[^1^][^2^][^3^][^4^][^5^]。
-
高度:对于包含n个节点的堆,其高度为O(log n),这使得堆的许多操作具有对数时间复杂度[^4^]。
varch heap是一个用于C语言的通用堆容器模块。它定义了堆相关的数据类型以及一系列操作堆的函数接口,涵盖了堆的创建、删除、元素入堆、出堆、修改、获取堆顶元素、获取堆大小等功能,同时通过宏定义提供了获取堆中节点父节点、左右子节点索引的便捷方式,方便开发者在C语言项目中运用堆这种数据结构来进行数据管理和操作,例如实现优先级队列等应用场景。
接口
创建和删除heap对象
heap_t heap_create(int dsize, int capacity, heap_root_t root);
void heap_delete(heap_t heap);
其中heap_t为heap的结构体。
创建方法: 创建方法则会返回一个heap对象,创建失败则返回NULL。
dsize:表示堆中每个元素的数据大小,以字节为单位,用于确定在内存中为每个元素分配的空间大小,比如存储一个包含多个成员的结构体元素时,该参数就应该设置为结构体的大小。capacity:堆的初始容量,也就是堆在创建时最多能容纳的元素个数,当后续插入元素超过这个容量时,可能需要进行扩容等相关处理(具体取决于内部实现逻辑),开发者可以根据预估的元素数量合理设置该参数。root:指向一个函数的指针,该函数用于定义堆的根节点类型规则,即通过比较父节点和子节点来确定堆的性质(大根堆或小根堆),传入的函数需要按照规定的返回值逻辑来实现,以便后续堆操作能依据正确的堆性质进行元素调整等操作。
删除方法: 删除传入的heap对象。创建方法和删除应该成对使用,创建出来在结束使用应该删除掉。
static int heap_root_min(void *parent, void *child)
{
if (*(int *)parent < *(int *)child) return 1;
return 0;
}
static int heap_root_max(void *parent, void *child)
{
if (*(int *)parent > *(int *)child) return 1;
return 0;
}
static void test_create(void)
{
heap_t heap = heap_create(sizeof(int), 11, heap_root_max);
if (heap)
{
printf("heap create success!!!\r\n");
}
else
{
printf("[ERROR] heap create fail!!!\r\n");
}
heap_delete(heap);
}
heap的入堆和出堆
int heap_push(heap_t heap, void *data);
int heap_pop(heap_t heap, void *data);
这两个方法可以很方便的把数据添加到堆和从堆弹出,push方法data传入需要入队数据的地址,pop方法data传入需要接收出队数据的地址,这两个方法data都可以传入NULL,那只是一个占位。操作成功返回1,失败返回0。
static void test_push(void)
{
heap_t heap = heap_create(sizeof(int), 11, heap_root_max);
int push = 0, top = 0;
push = 100; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top);
push = 1; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top);
push = 2; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top);
push = 200; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top);
push = -10; heap_push(heap, &push); heap_top(heap, &top); printf("top = %d\r\n", top);
heap_delete(heap);
}
heap的修改
int heap_modify(heap_t heap, int index, void *data);
用于修改堆中指定索引位置的元素数据,修改后会根据堆的性质重新调整堆的结构,确保堆依然保持正确的堆性质状态。若修改操作成功,函数返回1;若索引超出范围、堆不存在等情况导致修改无法进行,则返回0。
heap:堆的句柄,通过它来定位要修改元素所在的具体堆,只有对应正确的堆才能准确找到并修改目标元素。index:要修改的元素在堆中的索引位置,索引从0开始计数,表示堆中元素的序号,通过该索引能准确找到需要修改的目标元素,不过要确保传入的索引值在堆的有效范围内(小于堆的当前元素个数)。data:指向新数据的指针,该指针指向的数据内容会替换掉堆中指定索引位置原有的元素数据,其数据类型和大小应与创建堆时规定的元素数据大小一致。
static void test_base(void)
{
heap_t h = heap_create(sizeof(int), 11, heap_root_max);
int i = 0;
for (i = 0; i < 11; i++)
{
heap_push(h, &i);
}
i = -9;
heap_modify(h, 6, &i);
heap_delete(h);
}
heap的顶端
int heap_top(heap_t heap, void *data);
用于获取堆顶的元素数据,对于大根堆来说获取的是最大元素,对于小根堆则是最小元素。若获取操作成功,函数返回1,并且若data参数不为NULL,会将堆顶元素数据复制到该指针所指向的内存空间中;若堆为空或者出现其他导致获取失败的情况,则返回0。
heap:堆的句柄,通过该句柄确定要获取堆顶元素的目标堆,不同的堆有各自独立的堆顶元素,需要准确指定对应的堆才能获取正确的堆顶数据。data:指向用于存储堆顶元素数据的内存空间的指针,若希望获取堆顶元素内容并存储起来便于后续使用,可传入有效的指针地址,函数会进行数据复制操作;若不需要获取具体数据,可传入NULL作为参数。
heap的大小
int heap_size(heap_t heap);
用于获取当前堆中元素的个数,返回的整数值表示堆中实际存储的元素数量,方便开发者了解堆的当前存储情况,比如判断堆是否为空、是否已满等。
heap:堆的句柄,依据该句柄找到对应的堆数据结构,进而获取该堆所包含的元素个数信息,每个堆都有其独立的元素数量统计,通过句柄能准确区分不同堆的情况。