数据结构--堆(C语言实现)

一、堆

堆是一种完全二叉树结构 ,通常使用数组 实现,分为大堆小堆

大堆:父节点 ≥ 子节点,堆顶为最大值
小堆:父节点 ≤ 子节点,堆顶为最小值

堆的核心操作依赖向上调整与向下调整 ,常用于堆排序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");
}
  1. 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 函数实现逻辑:

该函数用于销毁堆,释放堆中动态申请的数组内存,将数组指针置空重置 sizecapacity0,避免内存泄漏和野指针

函数功能:销毁堆,释放动态申请的内存。

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 函数实现逻辑:

该函数删除大堆堆顶,先交换堆顶与末尾元素size1再对新堆顶向下调整,保证删除后仍为大堆

函数功能:删除大堆堆顶的数据

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);
    }
}
相关推荐
习惯就好zz2 小时前
RK3588 Android 12 修改 NTP 服务器:从资源覆盖到时间同步验证
android·运维·服务器·aosp·ntp
zopple11 小时前
Laravel9.X重磅升级:十大核心特性解析
android
Tomhex11 小时前
C语言内存安全防护指南
c语言
私人珍藏库11 小时前
【windows】跨平台 Android 刷机Root工具箱
android·windows·工具·刷机·软件·多功能
230万光年的思念13 小时前
zerotier连不上的问题
c语言
summerkissyou198713 小时前
Android-MediaSession-播放流程和例子
android·mediasession
私人珍藏库15 小时前
[Android] 蓝叠模拟器工具箱v1.1
android·智能手机·app·工具·软件·多功能
云霄IT16 小时前
安卓开发之java转dex再转smali
android·java·python
Fanfanaas16 小时前
Linux 基础开发工具(二)
linux·运维·服务器·c语言