【数据结构初阶】二叉树---堆

二叉树-堆的实现

    • 一、树的概念(什么是树)
    • 二、二叉树的概念及结构
        • 2.1 二叉树的概念
        • 2.2 二叉树的性质
        • 2.3 二叉树存储结构
    • 三、二叉树的顺序结构
        • 3.1 堆的概念及结构
        • 3.2 堆的向下调整算法
        • 3.3堆的创建
    • 四、堆的代码实现
        • 4.1 堆的初始化
        • 4.2 堆的销毁
        • 4.3 堆的插入
        • 4.4 堆的删除
        • 4.5 堆的判空及取堆顶数据
        • 4.6 测试

千里之行始于足下,老铁们看到这篇文章一定要认真耐心地看下去哟!

正文开始:

一、树的概念(什么是树)


    树是一种非线性数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。那么为什么把它叫作树呢?是因为它倒挂着就是树的形状,根朝上,叶朝下。

  • 树有一个特殊节点,就是根节点,也就是最顶上的没有前驱节点的那个节点
  • 除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i
    <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱节点,可以有0个或多个后继节点
  • 因此,树是递归定义的
    在这里插入图片描述
    ps:树的子树之间是不能有交集的,有交集的就不能称之为树。如下图:
    在这里插入图片描述
    树的基本术语
  • 节点的度 :一个节点含有的子树数量
  • 叶节点 :度为0的节点,即没有子节点的节点
  • 根节点 :没有父节点的节点
  • 父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点
  • 子结点:一个结点含有的子树的根结点称为该结点的子结点
  • 兄弟结点:具有相同父结点的结点互称为兄弟结点
  • 层 :根节点定义为第1层,其子节点为第2层,以此类推
  • 高度 :从指定节点到叶节点的最长路径的长度
  • 森林 :由多棵(两棵及以上)树组成的集合。

二、二叉树的概念及结构

2.1 二叉树的概念


    一棵二叉树是节点的一个有限集合,该集合:

  • 或者为空
  • 或者由一个根结点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述
2.2 二叉树的性质

    性质1:二叉树的第i层上至多有2i-1(i≥1)个节点
    性质2:深度为h的二叉树中至多含有2h-1个节点
    性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1
    性质4:具有n个节点的满二叉树深为log2(n+1)
    5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为i的结点有:

  • 若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
  • 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
  • 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
2.3 二叉树存储结构

    二叉树又可以分为顺序存储和链式存储两种:
1.顺序存储
    顺序存储就是使用数组结构来存储,而顺序存储一般只表示完全二叉树,否则会造成空间浪费的现象。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树
2.链式存储
    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 :

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
 struct BinTreeNode* left; // 指向当前结点左孩子
 struct BinTreeNode* right; // 指向当前结点右孩子
 BTDataType data; // 当前结点值域
}

三、二叉树的顺序结构

3.1 堆的概念及结构

    若有一个集合{k0,k1,k2…k(n-1)},将它所有的元素以完全二叉树的顺序存储形式于一个一维数组中,并满足:{k(i)<=k(2i+1)且k(i)<=k(2i+2)};或{k(i)>=k(2i+1)且k(i)>=k(2i+2)},则称为小堆或大堆。

堆的性质:

  • 堆中某个结点的值总是不大于或不小于其父结点的值
  • 堆总是一棵完全二叉树
    在这里插入图片描述
3.2 堆的向下调整算法

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

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

在这里插入图片描述

3.3堆的创建

    下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根结点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子结点的子树开始调整,一直调整到根结点的树,就可以调整成堆。

  • int a[ ] = {1,5,3,8,7,6};
    在这里插入图片描述

四、堆的代码实现


堆的底层逻辑:动态数组

#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
 
// 类型定义
typedef int HPDataTpye;
 
typedef struct Heap
{
	HPDataTpye* arr;
	int size;// 元素个数
	int capacity;// 空间容量
}Heap;
//初始化
void  HPInit(HP* php);
//交换头尾
void Swap(HPDataType* p1, HPDataType* p2);
//数据向下调整
void AdjustUp(HPDataType* a, int child);
//数据向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//销毁堆
void HPDestroy(HP* php);
//向堆添加数据
void HPPush(HP* php, HPDataType x);
//删除数据
void HPPop(HP* php);
//找堆顶数据
HPDataType HPTop(HP* php);
//判空
bool HPEmpty(HP* php);
4.1 堆的初始化

    堆的初始化方式是跟顺序表的初始化是一样的,因为堆使用的是顺序存储:

void HPInit(HP* php)
{
     assert(php);
     php->arr=NULL;
     php->size=php->capacity=0;
}
4.2 堆的销毁
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}
4.3 堆的插入

    堆的插入方式也跟顺序表的差不多,就是将新的节点插入数组的尾部,但是光插入还不行,还要继续调整节点的位置,因为插入节点后有可能使原本的堆变为数组而不是堆了:
在这里插入图片描述
具体的代码实现:

//数据向上调整
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;//利用数组下标找到parent的位置
	while (child > 0)//while(parent>=0)属于歪打正着
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//插入数据
void HPPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);//将数据插入后进行向上调整
}
4.4 堆的删除

    堆的删除删的是堆顶的数据,而删除堆顶的数据就没有想象中的这么简单了。删除堆顶的数据不能直接将其释放掉,若直接释放,根就没有了,只剩下左右两个堆,又要将堆重新建好,代价太大;这时我们应该将堆尾和堆顶数据换位置,再将堆尾的数据释放掉,然后将堆顶的数据向下调整直到重新成堆:
在这里插入图片描述
代码实现:

//数据向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//删除堆顶
void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	php->size--;

	AdjustDown(php->arr, php->size, 0);
}
4.5 堆的判空及取堆顶数据
//找堆顶数据
HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->arr[0];//直接将数组第一个数据返回即可
}
//判空
bool HPEmpty(HP* php)
{
	assert(php);

	return php->size == 0;//当size等于0时就返回空
}
4.6 测试
#include"Heap.h"
void TestHP01()
{
	
	HP hp;
	HPInit(&hp);
	int arr[] = { 4,2,8,1,5,6,9,7,3,2 };
	for (size_t i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		HPPush(&hp, arr[i]);
	}
	/*while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));

		HPPop(&hp);
	}*/
	HPDestroy(&hp);
}
int main()
{
	TestHP01();
	return 0;
}

调试结果(形成小堆):
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何从iconfont中获取字体图标并应用到微信小程序中去?

下面我们一一个微信小程序的登录界面的制作为例来说明&#xff0c;如何从iconfont中获取字体图标是如何应用到微信小程序中去的。首先我们看效果。 这里所有的图标&#xff0c;都是从iconfont中以字体的形式来加载的&#xff0c;也就是说&#xff0c;我们自始至终没有使用一张…

jenkins 自动化部署Springboot 项目

一、安装docker 1.更新yum命令 yum -y update2.查看机器有残留的docker服务&#xff0c;有就卸载干净 查看docker 服务 rpm -qa |grep docker卸载docker sudo yum remove docker-ce docker-ce-cli containerd.io sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/contai…

linux下的进程等待(wait、waitpid)

目录 引言 进程等待的必要性 见见猪跑&#xff1a;是什么 怎么办 多个子进程时 阻塞等待 非阻塞轮询 参数一&#xff1a; 参数二 进程等待的原理 进程退出相关的宏 第三个参数option&#xff08;设置等待的方式&#xff09; 引言 在Linux操作系统中&#xff0c;进程…

Jmeter实际应用

环境准备 JDK1.8Jmeter 5.6.3 下载地址Jmeter 插件 下载地址 放到lib/ext下 常用命令 # 启动 sh jmeter# 集群模式下启动节点&#xff0c;不启动用不了集群 sh jmeter-server#生成ssl需要的证书, 这里会要求输入个密码&#xff0c;是要在jmeter中用的 keytool -import -ali…

Claude Financial Data Analyst:基于Claude的金融数据分析工具!免费开源!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

基于SSM+小程序的垃圾分类管理系统(垃圾2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM小程序的垃圾分类管理系统实现了管理员及用户。 1、管理员功能结构图&#xff0c;管理员功能有个人中心&#xff0c;管理员管理&#xff0c;基础数据管理、论坛管理、垃圾信息管理…

钰泰ETA4553电压电平转换器IC

描述 ETA4553 是两位同相转换器&#xff0c;是一种双向电压电平转换器&#xff0c;可用于建立混合电压系统之间的数字开关兼容性。它使用两个独立的可配置电源轨&#xff0c;A 端口支持 1.65V 至 5.5V 的工作电压&#xff0c;同时跟踪 VCCA 电源&#xff0c;B 端口支持 2.3V 至…

QT QDialog::exec()调用时清除部件所有焦点

最近在做项目时&#xff0c;遇到一个问题&#xff1a;在统信UOS系统编写的QT程序&#xff0c;其中进入某些页面时&#xff0c;或者显示模态窗时&#xff0c;按钮都会有一个焦点框&#xff0c;这个是不允许的&#xff0c;于是乎&#xff0c;开始了清理焦点的旅途。 一、清理QDia…

高速自爆穿梭无人机技术详解

高速自爆穿梭无人机技术是一种结合了高速飞行与自爆式攻击能力的先进无人机技术。以下是对该技术的详细解析&#xff1a; 一、技术特点 1. 高速飞行&#xff1a; 高速自爆穿梭无人机通常具备极高的飞行速度&#xff0c;如部分型号的速度可达到174公里/小时&#xff0c;甚至更…

五,Linux基础环境搭建(CentOS7)- 安装Kafka

Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装Kafka 大家注意以下的环境搭建版本号&#xff0c;如果版本不匹配有可能出现问题&#xff01; 一、Kafka下载及安装 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高…

[ARC159D] LIS 2 题解

[ARC159D] LIS 2 题面&#xff1a; 题面翻译 给定 n n n 个操作&#xff0c;每次操作给出 l , r l,r l,r&#xff0c;并在 a a a 序列里依次添加 i ∈ [ l , r ] i\in[l,r] i∈[l,r]。 求最后 a a a 的最长上升子序列。 题目描述 数列 $ X $ があります。初め、$ X $ は空…

网络搜索引擎Shodan(1)

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;shodan(1)_哔哩哔哩_bilibili 本文主要讲解网络搜索引擎Shodan的一些用法&#xff08;host和search这两个命令&#xff09;。 Shodan 是一个网络…

Matlab学习02-matlab中的数据显示格式及符号变量

目录 一&#xff0c;关系运算和逻辑运算 二&#xff0c;变量 三&#xff0c;数据显示格式 四&#xff0c;符号运算 1&#xff0c;创建符号变量 2&#xff0c;数值矩阵转换成符号矩阵 一&#xff0c;关系运算和逻辑运算 在matlab中&#xff0c;只要数值不是 &#xff0…

jenkins下拉参数联动

需要安装Active Choices插件&#xff0c;官网地址&#xff1a; https://plugins.jenkins.io/uno-choice/ 安装完插件以后会出现Active Choices选项&#xff1a; 第一个参数&#xff1a; return ["dubbo-op-all-deployment1", "dubbo-op-all-deployment2",…

合并数组的两种常用方法比较

在 JavaScript 中&#xff0c;合并数组的两种常用方法是使用扩展运算符 (...) 和使用 push 方法。 使用扩展运算符 this.items [...this.items, ...data.items]; 优点&#xff1a; 易于理解&#xff1a;使用扩展运算符的语法非常直观&#xff0c;表达了“将两个数组合并成一个…

基于vue框架的的高校消防设施管理系统06y99(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;设备分类,设备信息,维修人员,报修信息,维修进度,院系,消防知识,培训记录,培训信息,备件信息,备件申请,派发信息,采购信息 开题报告内容 基于Vue框架的高校消防设施管理系统开题报告 一、项目背景与意义 随着高校规模的不断扩大和校园建…

基于Django+Python的房屋信息可视化及价格预测系统设计与实现(带文档)

项目运行 需要先安装Python的相关依赖&#xff1a;pymysql&#xff0c;Django3.2.8&#xff0c;pillow 使用pip install 安装 第一步&#xff1a;创建数据库 第二步&#xff1a;执行SQL语句&#xff0c;.sql文件&#xff0c;运行该文件中的SQL语句 第三步&#xff1a;修改源…

无人机喊话器详解!

喊话器材料 外壳常采用尼龙纤维增强材料&#xff0c;这种材料具有抗摔、抗震、轻便、灵活、质量稳定、操作简单等优点&#xff0c;能够满足不同场景的需求。 喊话范围 无人机喊话器的喊话范围主要取决于设备的型号、环境条件以及喊话器的性能参数。一般来说&#xff0c;无人…

【334】基于springboot的仓库管理系统

本科毕业设计论文 题目&#xff1a;仓库管理系统设计与实现 摘 要 信息内容数据从传统到当今&#xff0c;一直在改变&#xff0c;忽然互联网技术让传统信息内容管理见到划时代的黎明&#xff0c;由于传统信息内容管理从时效性、安全系数、可执行性等多个方面&#xff0c;碰到…

rsync算法原理

1. 简介 rsync是一种文件同步的工具&#xff0c;也是一种算法。 2. 算法原理 背景&#xff1a;计算机 α \alpha α 上有文件 a, 计算机 β \beta β上有文件b。要对这两个文件进行同步。 β \beta β将文件b分成大小为S字节的若干块&#xff0c;最后一份可能不足S字节对于b…