二叉树——堆的实现

news2024/11/15 9:51:00

一.前言

前面我们讲解了二叉树的概念以及二叉树的存储结构:https://blog.csdn.net/yiqingaa/article/details/139224974?spm=1001.2014.3001.5502

今天我们主要讲讲二叉树的存储结构,以及堆的实现。

二.正文

1.二叉树的顺序结构及实现

1.1二叉树的顺序结构

 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

1.2堆的概念及结构

如果有一个关键码的集合K = { k₀,k₁,k₂ ,…,k_(n-1)(注意:_()在这里表示()里是下标 ,如我们这里表示的是k的下标是n-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:K_(i) <=K_(2*i+1) 且K_(i) <=K_(2*i+2) (K_(i) >=K_(2*i+1) 且 K_(i)>=K_(2*i+2) ) i = 0,1,2…,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

堆的性质:

  1. 堆中某个结点的值总是不大于或不小于其父结点的值。
  2. 堆总是一棵完全二叉树。

值得注意的是:这里我们虽然把它想像成树的形状,但其实我们的底层是数组,我们是通过改变数组,来控制这棵“树”的。

打个比方来说:蚂蚁上树这道菜,这盘菜里面真的有蚂蚁吗?其实没有,只不过是人为想像成的而已。在这里的二叉树也是同样的道理。

 2.堆的实现

2.1堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

这里我们给了一个数组。

int array[] = {27,15,19,18,28,34,65,49,25,37};

//向下调整算法
void AdjustDown(HPDataType* php,int n ,int parent )//php是数组指针
{
	int child = parent * 2 + 1;
	if (php[child] > php[child + 1])
		child = parent * 2 + 2;
	while (child < n)
	{
		if (php[child] < php[parent])
		{
			Swap(&(php[child]), &(php[parent]));
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

2.2向上调整算法

 现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向上调整算法可以把它调整成一个小堆。

我们给出了一个数组:

int array[] = {15,18,19,25,28,34,65,49,27,37,2};

void AdjustUp(HPDataType* php,int child)//向上调整算法
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (php[child] < php[parent])
		{
			Swap(&(php[child]), &(php[parent]));
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

2.3堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

void HPPush(HP* ps,HPDataType x)//堆尾插入数据,ps是我们堆指针
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4: ps->capacity * 2;
		HPDataType* arr = (HPDataType*)realloc(ps->a,sizeof(HPDataType) * newcapacity);
		if (arr == NULL)
		{
			perror("realloc false");
			return;
		}
		ps->a = arr;
		ps->capacity = newcapacity;
	}
	ps->a[ps->size] = x;
	ps->size++;
	AdjustUp(ps->a,ps->size-1);
}

2.4堆的删除 

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

void HPPop(HP* ps)//删除堆顶数据
{
	assert(ps);
	assert(ps->size > 0);
	Swap(&(ps->a[0]), &(ps->a[ps->size - 1]));
	ps->size--;
	AdjustDown(ps->a,ps->size,0);
}

3.堆代码的实现

Heap.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
};
typedef struct Heap HP;
void HPInit(HP* ps);//堆的初始化
void HPDestroy(HP* ps);//堆的删除

void HPPush(HP* ps, HPDataType x);//堆尾插入数据
void HPPop(HP* ps);//删除堆顶数据

HPDataType HPTop(HP* ps);//取出堆顶数据
bool HPEmpty(HP* ps);//判断堆是否为空

void Swap(HPDataType* a, HPDataType* b);//交换两个数据
void AdjustUp(HPDataType* php, int child);//向上调整算法
void AdjustDown(HPDataType* php, int n, int parent);//向下调整算法
int HPSize(HP* ps);//返回堆的有效数据个数
void HPSort(HPDataType* php, int n);//堆排序

Heap.c 

#include"Heap.h"
void HPInit(HP* ps)//堆初始化实现
{
	assert(ps);
	ps->a = NULL;
	ps->size = ps->capacity = 0;

}

void HPDestroy(HP* ps)//堆销毁实现
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}

void Swap(HPDataType* a,HPDataType* b)//两个数据的交换
{
	HPDataType tmp =*a;
	*a = *b;
	*b = tmp;
}

void AdjustUp(HPDataType* php,int child)//向上调整算法
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (php[child] < php[parent])
		{
			Swap(&(php[child]), &(php[parent]));
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HPPush(HP* ps,HPDataType x)//堆尾插入数据
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4: ps->capacity * 2;
		HPDataType* arr = (HPDataType*)realloc(ps->a,sizeof(HPDataType) * newcapacity);
		if (arr == NULL)
		{
			perror("realloc false");
			return;
		}
		ps->a = arr;
		ps->capacity = newcapacity;
	}
	ps->a[ps->size] = x;
	ps->size++;
	AdjustUp(ps->a,ps->size-1);
}
void AdjustDown(HPDataType* php,int n ,int parent )//向下调整算法
{
	int child = parent * 2 + 1;
	if (php[child] > php[child + 1])
		child = parent * 2 + 2;
	while (child < n)
	{
		if (php[child] < php[parent])
		{
			Swap(&(php[child]), &(php[parent]));
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HPPop(HP* ps)//删除堆顶数据
{
	assert(ps);
	assert(ps->size > 0);
	Swap(&(ps->a[0]), &(ps->a[ps->size - 1]));
	ps->size--;
	AdjustDown(ps->a,ps->size,0);
}
bool HPEmpty(HP* ps)//判断堆是否为空
{
	assert(ps);
	if (ps->size == 0)
	{
		return true;
	}
	return false;
}
HPDataType HPTop(HP* ps)//取出堆顶数据
{
	assert(ps);
	assert(ps->size > 0);
	return ps->a[0];
}
HPDataType HPSize(HP* ps)//返回堆有效数据个数
{
	assert(ps);
	return ps->size;
}
void HPSort(HPDataType* php,int n)//堆排序
{
	//降序 建小堆
	//升序 建大堆
	//建堆的第一种方法
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(php, i);
	}*/
	//建堆的第二种方法:
	for (int i = (n - 1 - 1) / 2; i < n; i++)//(n-1-1)/2是为了找最后一个数据的父亲
	{
		AdjustDown(php, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&(php[0]), &(php[end]));
		AdjustDown(php, end, 0);
		end--;
	}
}


test.c 

#include"Heap.h"
void test01()
{
	HP hp;
	HPInit(&hp);
	HPPush(&hp, 6);
	HPPush(&hp, 2);
	HPPush(&hp, 3);
	HPPush(&hp, 4);
	HPPush(&hp, 10);
	HPPush(&hp, 9);
	HPPush(&hp, 7);
	HPPop(&hp);
	printf("%d\n", HPTop(&hp));
	printf("%d\n", HPSize(&hp));
//	HPDestroy(&hp);
}
void test02()
{
	int arr[] = { 1,2,6,4,5,8,9,7 };
	HPSort(&arr,sizeof(arr)/sizeof(int));
}
int main()
{
	//test01();
	test02();
	return 0;
}

值得注意的是test.c是本人为了测试各函数是否正常而写出来的。

三.结言

 堆的分享就到这了。觉得对自己有帮助的同学,可不可以给个三连。

好啦,同学们我们下期再见,拜拜喽。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1702413.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五款屏幕监控软件测评,哪一款屏幕监控软件适合企业

市场上有多款屏幕监控软件&#xff0c;其中一些因其实用的功能、良好的用户体验和广泛的用户基础而获得了较高的市场占有率和口碑。以下几款是较为受欢迎且功能全面的屏幕监控软件&#xff1a; 1、安企神软件&#xff1a; 领取7天试用版&#xff1a;https://work.weixin.qq.co…

【NumPy】关于numpy.sort()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

一致 VS 正确

“代码命名时&#xff0c;一致大于正确” 本文适合以下小伙伴阅读&#xff1a; 经常会有疑惑&#xff1a;这谁的单词拼错了&#xff0c;我之后要将错就错吗&#xff1f;时常感觉到&#xff1a;项目中的命名好乱啊&#xff0c;明明是一个东西怎么一堆不同的名字 好了&#xf…

企业选择定制化MES管理系统时需要考虑的核心功能

在当今制造业的数字化转型浪潮中&#xff0c;企业对于实现生产现场透明管理的需求愈发迫切。为了满足这一需求&#xff0c;MES管理系统成为了众多企业的首选解决方案。MES管理系统以其高度的灵活性和可定制性&#xff0c;能够根据不同行业的特性&#xff0c;为企业提供量身定制…

告别繁琐!Xinstall助你轻松实现APP地推结算,提升推广效率

随着移动互联网的迅猛发展&#xff0c;APP市场竞争日益激烈。面对线上推广转化率下降、成本上升的挑战&#xff0c;越来越多的APP厂商开始尝试线下地推这一更为直接、有效的推广方式。然而&#xff0c;地推结算过程中的种种问题却让许多企业头痛不已。今天&#xff0c;我们将为…

信息化项目必须进行验收测试吗?软件测试公司验收测试流程分享

信息化项目验收是指在软件开发完成之后&#xff0c;对其进行独立检查和确认&#xff0c;以确定它是否达到了预期的质量和功能需求。在进行验收之前&#xff0c;必须进行验收测试&#xff0c;这是非常重要的一步。 为什么要进行验收测试呢&#xff1f;好处可不少哦&#xff01;…

【uniapp】CSS实现多行文本展开收起的文字环绕效果

1. 效果图 收起状态 展开状态 2. 代码实现 <view class"word-wrap" id"descriptionTxt"><view class"fold-text" v-if"isFold"><text class"fold-btn" click"changFold">全文</text&g…

人工智能应用层岗位—AI项目经理/AI产品经理

AI是一门技术&#xff0c;最终落实成产品才能具备商业价值 应用层&#xff0c;就是面向特定应用场景&#xff0c;形成人工智能软硬件产品或解决方案。主要包括行业AI解决方案和热门产品&#xff0c;如自动驾驶、机器人、智能家居、可穿戴的智能设备等 相应的&#xff0c;就会…

猫狗分类识别模型建立①数据标记

一、labelImg库说明 LabelImg是一款非常流行的图像标注工具&#xff0c;广泛用于机器学习和计算机视觉领域。以下是关于LabelImg的详细介绍&#xff1a; 主要功能和特点 1.图像标注 允许用户在图像中标注物体&#xff0c;选择特定区域&#xff0c;并为这些区域添加标签或类…

C语言基础——数组

{\▁/} ( / 。\ ) / ⊃&#x1f494;\⊃ 为什么我那么努力还是得不到那么多赞 ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;C语言基础&#xff1b; 文章目录 前言…

Linux基础知识点总结!超详细

Linux 的学习对于一个IT工程师的重要性是不言而喻的&#xff0c;学好它是工程师必备修养之一。 Linux 基础 操作系统 操作系统Operating System简称OS&#xff0c;是软件的一部分&#xff0c;它是硬件基础上的第一层软件&#xff0c;是硬件和其它软件沟通的桥梁。 操作系统…

部署Prometheus + Grafana实现监控数据指标

1.1 Prometheus安装部署 Prometheus监控服务 主机名IP地址系统配置作用Prometheus192.168.110.27/24CentOS 7.94颗CPU 8G内存 100G硬盘Prometheus服务器grafana192.168.110.28/24CentOS 7.94颗CPU 8G内存 100G硬盘grafana服务器 监控机器 主机名IP地址系统配置k8s-master-0…

链接预测.

在某些场景中&#xff0c;用户可能希望预测给定节点之间是否存在边&#xff0c;这样的任务称作链接预测任务。 假设输入结点之间是全链接图&#xff0c;连接预测的目的是给边打上标签。 挑战 最简单的是用图的邻接矩阵来表示结点之间的边&#xff0c;但当节点数量多的时候&am…

【C++】从零开始map与set的封装

送给大家一句话&#xff1a; 今日的事情&#xff0c;尽心、尽意、尽力去做了&#xff0c;无论成绩如何&#xff0c;都应该高高兴兴地上床恬睡。 – 三毛 《亲爱的三毛》 &#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x…

自适应容积卡尔曼滤波|(自适应CKF)的MATLAB源代码

介绍 容积卡尔曼滤波在理论上拥有比UKF更高的精度和稳定性&#xff0c;本自适应算法通过对观测残差的计算&#xff0c;在观测协方差R不准确或无法获得时&#xff0c;对R进行调节&#xff0c;以起到降低估计误差的作用。 模型 使用的是三维的非线性模型&#xff0c;经过适当修…

Hunyuan-DiT环境搭建推理测试

引子 最近鹅厂竟然开源了一个多模态的大模型&#xff0c;之前分享福报厂的多模态视觉大模型&#xff08;Qwen-VL环境搭建&推理测试-CSDN博客&#xff09;感兴趣的可以移步。鹅厂开源的&#xff0c;我还是头一回部署。好的&#xff0c;那就让我们看看这个多模态视觉大模型有…

今日选题。

诱导读者点开文章的9引真经&#xff08;一&#xff09; 标题重要么&#xff1f;新媒体、博客文通常在手机上阅读。首先所有的内容不同于纸媒&#xff0c;手机只展现标题&#xff0c;而内容都是折叠。其次读者能像看内容一样看4、5条或者7、8条标题&#xff08;区别于不同的主流…

LayUI使用(一)点击树组件的右边空白区域也可响应事件

前提&#xff1a; 如下&#xff0c;希望能够点击右边的空白区域也能够响应&#xff0c;而不仅仅是点击文本才响应 分析流程 一开始问了chatgpt&#xff0c;但它给的方案太麻烦了&#xff0c;而且还有错误&#xff0c;因此自己上手F12进入调试模式&#xff0c;点击查看最终渲…

SpringBoot——整合SLF4j进行日志记录

目录 日志 项目总结 新建一个SpringBoot项目 pom.xml application.properties项目配置文件 logger.xml日志配置文件 TestController控制器 SpringbootSlf4jApplication启动类 启动项目 生成logger.log日志文件 日志 在开发中&#xff0c;我们经常使用 System.out.prin…

会声会影2023永久激活版下载 会声会影2023序列号免费 会声会影下载免费中文破解版

会声会影2023永久激活版是一款最新推出的多功能视频剪辑软件&#xff0c;这款软件不仅完美继承了之前多个版本当中的强大功能。而且我们还可以通过会声会影2023永久激活版来体验到标题动态选项、标题特效等多个全新的功能&#xff0c;让你可以更加快速地进行视频编辑。 会声会影…