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

二叉树中堆的实现

1 堆的声明和定义

typedef int HPDateType;
typedef struct Heap {
	HPDateType* arr;
	int size;
	int capcity;
}HP;

与顺序表相似,我们需要一个数组,有效空间大小,有效元素个数

2 堆的初始化

void HPInit(HP*php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capcity = 0;
}

把数组置空,有效元素个数和有效空间大小置为0

3 堆的销毁

void HPDestroy(HP* php)
{
	assert(php);
	if (php->arr)
		free(php->arr);
	php->arr = NULL;
	php->capcity = php->size = 0;
}

传递的参数当然不能为空,用assert断言,接着把arr的空间释放掉,让size,capcity为0

4 入堆

这里我们需要先讲解思路

首先,堆是用数组储存起来的,如果直接插到数组最后一位是不合理的 

如图所示,可能空间是满的,这里就需要们重新开辟空间

 同时,为了保证堆为一个有效堆(保证为大堆或者小堆),我们需要重新调整堆的排序

空间开辟

if (php->size == php->capcity)
{
	int newcapcity = php->capcity == 0 ? 4 : 2 * php->capcity;
	HPDateType* tmp = (HPDateType*)realloc(php->arr, newcapcity * sizeof(HPDateType));
	if (tmp == NULL)
	{
		perror("realloc fail");
		exit(1);
	}
	php->capcity = newcapcity;
	php->arr = tmp;
}

利用realloc开辟,最后给capcity和arr赋值

堆调整--向上调整--小堆

void AdjustUp(HPDateType* arr, int child)//向上调整
{
	while (child > 0)
	{
		int parent = (child - 1) / 2;
		// <: 小堆
		// >: 大堆
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);//swap函数的形参是两个指针,需要传地址
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}
}

调整原理:将该入堆元素(child)插到末尾,顺着其父母结点(parent)往上调整,在堆为小堆的条件下,如果该元素比他的父母结点小就交换二者,再让其父母结点成为新的孩子结点,循环往复,直到新的孩子结点跳出,或者直白点说就是下标<0就跳出循环

注意,因为当child走到根结点时也需要比较之后判断是否要交换,所以不能只是parent走到空,必须要child走到空

完整入堆操作的实现

void HPPush(HP* php, HPDateType x)
{
	assert(php);
	//判断空间是否足够	
	if (php->size == php->capcity)
	{
		int newcapcity = php->capcity == 0 ? 4 : 2 * php->capcity;
		HPDateType* tmp = (HPDateType*)realloc(php->arr, newcapcity * sizeof(HPDateType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(1);
		}
		php->capcity = newcapcity;
		php->arr = tmp;
	}
	php->arr[php->size] = x;
	AdjustUp(php->arr, php->size);//向上调整
	++php->size;
}

如果是大堆的调整就把向上调整的" < "改为" > " 

最后还需要让最大元素个数+1

5 出堆

思路讲解这里出堆出的通常是堆顶元素,如果要出堆尾元素只需要让size--即可,意义不大,如果是出堆顶元素,那么这里我们一定不能用顺序表的向前覆盖来写,这样会让整个堆的结构混乱,我们不妨另辟蹊径,继续沿用交换操作,让根结点和最后一个结点交换,再出堆尾让size--,这时候我们关注交换到堆顶的元素,利用向下调整算法,让其成为有效堆

堆调整--向下调整--大堆

void AdjustDown(HPDateType*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;
		}
		
	}

}

调整原理从堆顶开始,此时的堆顶设为初始parent,我们取child结点中较大的一个作为比较对象,如过child>parent就交换,再让child成为新的parent,直到调整完成跳出循环,或者child走到最后一个结点

这里我们还需要注意child+1在调整的时候是否越界,以防出现非法访问

 完整出堆操作的实现

void HPPop(HP* php) 
{
	assert(!HPEmpty(php));
	swap(&php->arr[0], &php->arr[php->size - 1]);
	--php->size;
	AdjustDown(php->arr, 0, php->size);
}

其中判空函数

bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

6 借助数据结构堆来实现的堆排序

先来写取堆顶元素的实现

HPDateType HPTop(HP* php)
{
	assert(!HPEmpty(php));
	return php->arr[0];
}
void HeapSort01(int* arr, int n)
{
	HP hp;
	HPInit(&hp);
	for (int i = 0; i < n; i++)
	{
		HPPush(&hp,arr[i]);
	}
	int i = 0;
	while (!HPEmpty(&hp))
	{
		int top = HPTop(&hp);
		arr[i++] = top;
		HPPop(&hp);//删除之后会重新排列
	}
	HPDestroy(&hp);
}

这种堆排序借助了入堆和出堆时的堆调整,因为每次出堆我们都获取了堆中最小或者最大的元素,所以最终得到了一个有序序列

7 常规堆排序

思路讲解:首先利用向下堆调整让待排序的数组建堆,接着将堆顶元素和最后一个元素交换,接着再进行堆调整直到end<0,实质上还是堆顶元素一定为最大(小)的出堆操作的运用

有如下示例帮助理解

这里我们实现了把最大元素一个个放到末尾 ,最终实现堆排序

void HeapSort(int* arr,int n)
{
	//向下调整建堆
	for (int i = (n - 2) / 2; i>=0;i--)
	{
		AdjustDown(arr, i, n);//i是最后一个节点的parent节点
	}
	int end = n - 1;
	while (end>0)
	{
		swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}


http://www.kler.cn/a/584817.html

相关文章:

  • ubuntu 24 安装 python3.x 教程
  • 【网络编程】HTTP网络编程
  • Mysql_DML
  • Machine Learning中的模型选择
  • 关于分布式的误区
  • STM32定时器配置1毫秒中断
  • Node.js Web 模块详解
  • 【原创】在高性能服务器上,使用受限用户运行Nginx,充当反向代理服务器[未完待续]
  • 接口自动化入门 —— JSON中的万能密码--JSONPath解析!
  • 基于javaweb的SpringBoot个人健康管理系统小程序微信小程序设计与实现(源码+文档+部署讲解)
  • Java 实现 Android ViewPager2 顶部导航:动态配置与高效加载指南
  • 【SpringBoot】MD5加盐算法的详解
  • RabbitMQ 实现原理及流程
  • 【数据结构】-哈夫曼树以及其应用
  • 【原创】springboot+vue校园新冠疫情统计管理系统设计与实现
  • git切换版本
  • 根据开始和结束日期,获取每一天和每个月的开始和结束日期的list
  • 深度对话:AI界的奥本海默与通用人工智能(AGI)的未来
  • 如何在Futter开发中做性能优化?
  • 前端面试:React生态有哪些?