数据结构(二叉树-1)

news2024/9/24 0:05:59

文章目录

一、树

  1.1 树的概念与结构

  1.2 树的相关术语

  1.3 树的表示

二、二叉树

  2.1 二叉树的概念与结构

  2.2特殊的二叉树

    满二叉树

    完全二叉树

  2.3 二叉树的存储结构

三、实现顺序结构二叉树

  3.1 堆的概念与结构

  3.2 堆的实现

  Heap.h

  Heap.c

    默认初始化堆

    堆的销毁

    堆的插入

  删除堆顶数据


一、树

1.1 树的概念与结构

  • 树是⼀种⾮线性的数据结构,它是由 nn>=0) 个有限结点组成⼀个具有层次关系的集合。把它叫做 树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。
  • 有⼀个特殊的结点,称为根结点,根结点没有前驱结点。
  • 除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1T2……Tm ,其中每⼀个集合 Ti(1 <= i <= m) ⼜是⼀棵结构与树类似的⼦树。每棵⼦树的根结点有且只有⼀个前驱,可以0 个或多个后继。因此,树是递归定义的。

 树形结构中,⼦树之间不能有交集,否则就不是树形结构

  •  ⼦树是不相交的(如果存在相交就是图了,图以后得课程会有讲解)
  • 除了根结点外,每个结点有且仅有⼀个⽗结点
  • ⼀棵N个结点的树有N-1条边

1.2 树的相关术语

  •  ⽗结点/双亲结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点; 如上图:A是B的⽗结点
  • ⼦结点/孩⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点
  • 结点的度:⼀个结点有⼏个孩⼦,他的度就是多少;⽐如A的度为6,F的度为2,K的度为0
  • 树的度:⼀棵树中,最⼤的结点的度称为树的度; 如上图:树的度为 6
  • 叶⼦结点/终端结点:度为 0 的结点称为叶结点; 如上图: BCHI... 等结点为叶结点
  • 分⽀结点/⾮终端结点:度不为 0 的结点; 如上图: DEFG... 等结点为分⽀结点
  • 兄弟结点:具有相同⽗结点的结点互称为兄弟结点(亲兄弟); 如上图: BC 是兄弟结点结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推;
  • 树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为 4
  • 结点的祖先:从根到该结点所经分⽀上的所有结点;如上图: A 是所有结点的祖先
  • 路径:⼀条从树中任意节点出发,沿⽗节点-⼦节点连接,达到任意节点的序列;⽐如A到Q的路径为:A-E-J-Q;H到Q的路径H-D-A-E-J-Q
  • ⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
  • 森林:由 mm>0) 棵互不相交的树的集合称为森林;

1.3 树的表示

  孩⼦兄弟表⽰法:

struct TreeNode
 {
     struct Node* child; // 左边开始的第⼀个孩⼦结点
     struct Node* brother; // 指向其右边的下⼀个兄弟结点
     int data; // 结点中的数据域
 };

二、二叉树 

2.1 二叉树的概念与结构

在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空。  

从上图可以看出⼆叉树具备以下特点:
  1.  ⼆叉树不存在度⼤于 2 的结点
  2.  ⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树
注意:对于任意的⼆叉树都是由以下⼏种情况复合⽽成的:
 

2.2特殊的二叉树

满二叉树

 ⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果⼀个⼆叉树的层数为 K ,且结点总数是 2 k − 1 ,则它就是满⼆叉树。

完全二叉树

 完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为 K 的,有 n 个结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 n 的结点⼀⼀对应时称之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。

(假设二叉树层次为 k ,除了第 k 层外,每层结点的个数达到最大结点数,第 k 层结点个数不一定达到最大结点数)

⼆叉树性质
根据满⼆叉树的特点可知:
1)若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2 i −1 个结点
2)若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 2 h − 1 3)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 ( log
以2为底, n+1 为对数)

 2.3 二叉树的存储结构

⼆叉树⼀般可以使⽤两种结构存储,⼀种顺序结构,⼀种链式结构。
下面我们先来使用顺序结构实现二叉树。

  • 顺序结构存储就是使⽤数组来存储,⼀般使⽤数组只适合表⽰完全⼆叉树,因为不是完全⼆叉树会有空间的浪费,完全⼆叉树更适合使⽤顺序结构存储。

现实中我们通常把堆(⼀种⼆叉树)使⽤顺序结构的数组来存储,需要注意的是这⾥的堆和操作系统虚拟进程地址空间中的堆是两回事,⼀个是数据结构,⼀个是操作系统中管理内存的⼀块区域分段。

三、实现顺序结构二叉树

⼀般堆使用顺序结构的数组来存储数据,堆是⼀种特殊的⼆叉树,具有⼆叉树的特性的同时,还具备其他的特性。

3.1 堆的概念与结构

如果有⼀个关键码的集合 ,把它的所有元素按完全⼆叉树的顺序存储⽅式存储,在⼀个⼀维数组中,并满⾜: ( 且 ), i = 0、1 2... ,则称为⼩堆(或⼤堆)。将根结点最⼤的堆叫做最⼤堆或⼤根堆,根结点最⼩的堆叫做最⼩堆或⼩根堆。

小根堆                                                        大根堆

  • 堆中某个结点的值总是不⼤于或不⼩于其⽗结点的值;
  • 堆总是⼀棵完全⼆叉树。
⼆叉树性质
  • 对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从0 开始编号,则对于序号为 i 的结点有:
  1. i>0 i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,⽆双亲结点
  2. 若 2i+1<n ,左孩⼦序号: 2i+1 2i+1>=n 否则⽆左孩⼦
  3. 2i+2<n ,右孩⼦序号: 2i+2 2i+2>=n 否则⽆右孩⼦

 3.2 堆的实现

以小根堆为例子:

Heap.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//定义堆的结构---数组

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* arr;
	int size;//有效的数据个数
	int capacity;//空间大小
}HP;

//初始化
void HPInit(HP* php);

//销毁
void HPDestroy(HP* php);

//堆的插入
void HPPush(HP* php, HPDataType x);

//出堆,删除堆顶数据
void HPPop(HP* php);

//返回堆顶数据
HPDataType HPTop(HP* php);

// 判空
bool HPEmpty(HP* php);

Heap.c

默认初始化堆
void HPInit(HP* php)
{
	assert(php);
	php->arr = NULL;
	php->capacity = php->size = 0;
}

堆的销毁
void HPDestroy(HP* php)
{
	assert(php);
	if (php->arr)
	{
		free(php->arr);
		php->arr = NULL;

		php->capacity = php->size - 0;
	}
}
堆的插入

    如果要在下一个数据 “50” 到 arr【6】的位置上就不满足小堆的特性,

此时我们就要用到:堆的向上调整算法

向上调整算法
先将元素插⼊到堆的末尾,即最后⼀个孩⼦之后
插⼊之后如果堆的性质遭到破坏,将新插⼊结点顺着其双双亲往上调整到合适位置即可

void swap(int* x, int* y)
{
	int z = *x;
	*x = *y;
	*y = z;
}

void Adjustup(HPDataType* arr, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (arr[child] < arr[parent])             //如果插入的数据大小小于他的父节点
		{
			swap(&arr[child], &arr[parent]);      //交换
			child = parent;                       //交换后的孩结点来到原来他的父节点的位置  
			parent = 2 * child - 1;
		}
		else
		{
			break;
		}
	}
}

void HPPush(HP* php, HPDataType x)
{
	assert(php);

	//判断空间是否足够
	if (php->size == php->capacity)
	{
		//扩容
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr, newcapacity * sizeof(HPDataType));
	
		if (tmp  == NULL)
			{
				perror("realloc fail!");
				exit(1);
			}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;
	php->size++;

	Adjustup(php->arr, php->size - 1);
}

 

 删除堆顶数据

    如果直接删除 arr【0】,就会改变原先堆的结构,所以我么可以先先将头和尾的数据交换,在删除 arr【5】,但是又有问题出现。交换删除后的数据有可能不满足小堆的特性,此时就要用到:堆的向下调整算法 

向下调整算法
将堆顶元素与堆中最后⼀个元素进⾏交换
删除堆中最后⼀个元素  
将堆顶元素向下调整到满⾜堆特性为⽌

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

void HPPop(HP* php)
{
	assert(php && php->size);

	         //arr[0]         arr[size-1]
	swap(&php->arr[0], &php->arr[php->size - 1]);

	php->size--;

	AdjustDown(php->arr, 0, php->size);


}

test.c

最后测试一下代码的实现

#include"Heap.h"

void Hptest()
{
	HP hp;
	HPInit(&hp);

	int arr[] = { 17,25,60,54,30,70 };
	
	for (int i = 0; i < 6; i++)
	{
		HPPush(&hp, arr[i]);
	}

	HPPop(&hp);
}


int main()
{
	Hptest();
	return 0;
}

3.3 堆的应用

堆排序 

方法一:基于已有数组建堆、取堆顶元素完成排序版本

// 1、需要堆的数据结构
// 2、空间复杂度 O(N)
void HeapSort(int* a, int n)
{
    HP hp;
    for(int i = 0; i < n; i++)
    {
            HPPush(&hp,a[i]);
    }
    int i = 0;
    while (!HPEmpty(&hp))
    {
        a[i++] = HPTop(&hp);
        HPPop(&hp);
    }
   HPDestroy(&hp);
}

方法二:

// 升序,建⼤堆
// 降序,建⼩堆
// O(N*logN)
void HeapSort(int* a, int n)
 {
 // a数组直接建堆 O(N)
 for (int i = (n-1-1)/2; i >= 0; --i)
 {
 AdjustDown(a, n, i);
 }

 // O(N*logN)
 int end = n - 1;
 while (end > 0)
 {
 Swap(&a[0], &a[end]);
 AdjustDown(a, end, 0);
 --end;
 }
 }

未完待续~ 

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

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

相关文章

关于使用宝兰德bes中间件进行windows部署遇到的问题——license不存在

报错信息 日志文件中是这么报错的 遇到的具体情况&#xff1a; 实例按照**的文档手册正常步骤下去节点部署的时候没有报错&#xff0c;成功启动&#xff0c;但是日志里会有报错信息&#xff0c;也是license不存在实例创建的时候失败了&#xff0c;报错信息如下所示 解决方法…

Gitops-Argo-Cli安装与使用

一、安装Argo-Cli工具 Release v2.9.21 argoproj/argo-cd GitHub **选择合适的符合你操作系统以及CPU架构的二进制文件 #依v2.9.21-X86-64-Linux操作系统为例 wget https://github.com/argoproj/argo-cd/releases/download/v2.9.21/argocd-linux-amd64 #添加执行权限并且移…

昇思25天学习打卡营第19天|生成式-DCGAN生成漫画头像

打卡 目录 打卡 GAN基础原理 DCGAN原理 案例说明 数据集操作 数据准备 数据处理和增强 部分训练数据的展示 构造网络 生成器 生成器代码 ​编辑 判别器 判别器代码 模型训练 训练代码 结果展示&#xff08;3 epoch&#xff09; 模型推理 GAN基础原理 原理介…

AV1技术学习:Loop Restoration Filter

环路恢复滤波器&#xff08;restoration filter&#xff09;适用于64 64、128 128 或 256 256 像素块单元&#xff0c;称为 loop restoration units (LRUs)。每个单元可以独立选择是否跳过滤波、使用维纳滤波器&#xff08;Wiener filter&#xff09;或使用自导滤波器&#…

AM62x和rk3568的异同点

AM62x 和 RK3568 是两款不同的处理器&#xff0c;分别来自 Texas Instruments&#xff08;TI&#xff09;和 Rockchip。它们在设计目标、架构、性能和应用领域等方面存在一些异同。以下是这两款处理器的对比&#xff1a; 1. 基本架构 AM62x&#xff1a; 架构&#xff1a;基于…

【云原生】Kubernetes 中的 PV 和 PVC 介绍、原理、用法及实战案例分析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

CRM客户管理系统是什么?如何利用CRM盘活老客户?

人与人之间的差距&#xff0c;不仅在于业务能力的高低&#xff0c;更在于如何高效地管理客户、建立深厚的客户关系。在这个“内卷化”严重的时代&#xff0c;借助工具来管理客户成为必不可少的流程。如果你保持怀疑态度&#xff0c;那我们先来聊聊。 客户管理是什么&#xff1…

HormonyOs之 路由简单跳转

Navigation路由相关的操作都是基于页面栈NavPathStack提供的方法进行&#xff0c;每个Navigation都需要创建并传入一个NavPathStack对象&#xff0c;用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。 Entry Component struct Index …

探索 Electron:快捷键与剪切板操作

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

Mysql9安装

目录 一、下载mysql 二、安装 三、配置mysql环境变量 四、mysql初始化和启动 1.以管理员身份运行cmd 2.cd到mysql的安装目录 3.初始化mysql的数据库 4.为Windows系统安装MySQL服务 5.查看一下名为mysql的服务&#xff1a; 6.启动MySQL服务 五、附录 1.系统变量还在&…

grafana对接zabbix数据展示

目录 1、初始化、安装grafana 2、浏览器访问 3、安装zabbix 4、zabbix数据对接grafana 5、如何导入模板&#xff1f; ① 设置键值 ② 在zabbix web端完成自定义监控项 ③ garafana里添加nginx上面的的三个监控项 6、如何自定义监控项&#xff1f; 以下实验沿用上一篇z…

arm环境下构建Flink的Docker镜像

准备工作 资源准备 按需下载 flink&#xff0c;我的是1.17.2版本。官方说1.13版本之后的安装包兼容了arm架构&#xff0c;所以直接下载就行。 如需要cdc组件&#xff0c;提前下载好。 服务器准备 可在某云上购买arm服务器&#xff0c;2c/4g即可&#xff0c;按量付费。 带宽…

2024datawhale电力需求预测挑战赛

电力需求预测挑战赛 比赛链接&#xff1a;https://challenge.xfyun.cn/topic/info?typeelectricity-demand&optionssgy&chdw24_uGS8Gs 学习链接&#xff1a;https://datawhaler.feishu.cn/wiki/CuhBw9vBaiG1nJklIPkcRhqVnmk 一句话介绍赛题任务可以这样理解赛题&am…

【Vue实战教程】之Vuex状态管理详解

Vuex状态管理 1 Vuex简介 1.1 什么是Vuex Vuex是一个专为Vue.js应用程序开发的状态管理工具。它采用了集中式存储管理应用的所有的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 简单来说&#xff0c;Vuex是一个适用于在Vue项目开发时使用的状态管理…

Nginx反向代理概述

正向代理与反向代理概述 正向代理&#xff1a; 定义&#xff1a;正向代理位于客户端和目标服务器之间&#xff0c;客户端的请求首先发送到代理服务器&#xff0c;然后由代理服务器转发到目标服务器&#xff0c;最后将目标服务器的响应返回给客户端。 作用&#xff1a;正向代理…

逻辑回归推导

逻辑回归既可以看作是回归算法&#xff0c;也可以看做是分类算法。通常作为分类算法使用&#xff0c;只可以解决二分类问题。 在上述平面中&#xff0c;每个颜色代表一个类别&#xff0c;即有4个类别 将红色的做为一个类别&#xff0c;其他三个类别都统称为其他类别&#xff0…

javascript 的上下文与作用域

目录 1. 初步了解 上下文&#xff08;context&#xff09;2. 全局上下文(global context)3. 上下文栈 (context stack)4. 作用域链( scope chain)5. 作用域(scope)6. 作用域链增强7. 变量声明7.1 var 声明变量7.2 let 声明变量7.3 const 常量声明 8. 标识符查找总结 带着疑问去…

D3.高精度

1.分类情况 AB、A-B、A*a、A/b A和B指的是超大超长整数&#xff0c;长度<1e6; a的值<10000&#xff1b; 2.大整数的存储 int 变量肯定是存不了这么大的数的&#xff0c;做法是将大整数先存到string字符串&#xff0c;再使用字符串的访问方式&#xff0c;将每一位数存到…

C++树形结构(3 树的中心、重心)

目录 一.树的中心&#xff1a; 1.树的概念&#xff1a; 2.树的性质&#xff1a; 性质1&#xff1a; 性质2&#xff1a; 3.树的中心求解&#xff1a; 4.例题&#xff1a; 二.树的重心&#xff1a; 1.基础概念&#xff1a; 2.求解方法&#xff1a; 3.例题&#xff1a;…

运筹学笔记

计算的时间问题&#xff01;计算机解决了计算量的问题&#xff01; 计算机的发展对运筹学研究起到了极大的促进作用。 运筹学的一个特征之一是它常常会考虑寻求问题模型的最佳解决方案&#xff08;称为最优解&#xff09;。 没有人能成为运筹学所有方面的专家。 分析学越来越流…