[数据结构——lesson8.树]

目录

引言

学习目标

1.树的概念及结构

1.1树的定义

1.2树的基本概念

1.3 树的表示

(1)双亲表示法

(2)孩子表示法

(3)左孩子右兄弟表示法

1.4 树在实际中的运用(表示文件系统的目录树结构)

结束语:


引言

之前我们学习了栈和队列数据结构,接下来我们学习一种新的数据结构——

学习目标

  • 为什么要学习为什么要学习非线性结构 ---- 树(Tree)
  • 数的定义和基本概念
  • 树的表示
  • 树的实际应用
  • 树的性质

为什么要学习非线性结构 ---- 树(Tree)

线性结构的优缺点

在线性结构中,无论是顺序存储,还是链式存储,线性表均有其优缺点:

  • 顺序表优点:
    1:顺序存储可以在 O(1) 的时间内找到特定次序的元素(下标的随机访问)
    2:CPU 高速缓存,命中率较高
  • 顺序表缺点: 
    1:顺序存储在,数据中间、头部 的插入和删除元素需要挪动大量元素,需要时间O(n)
    2:  顺序存储时,会出现空间不足,只能进行空间的扩容(异地扩容代价比较大)

  • 链表优点:
    1:在任意位置进行数据的插入和删除的效率高,所需时间为O(1)
    2:  按需申请空间和释放,不存在扩容
  • 链表的缺点:
    1:在寻找特定次序的元素需要从链表头部向后查找,需要时间O(n)
    2:  CPU高速缓存,命中率低 


 其实链表和顺序表是一个互补的数据结构
 

优化方案 ----- 树(Tree)

树形结构:很好的结合了顺序表和链表的优点,可以在O(logn)的时间内完成查找、更新、插入、删除等操作,在实际的应用中,很多算法可以借助于树形结构高效的实现很多功能。

树的讲解流程

此时此刻大家肯定很想了解什么是树,在本篇博客中,并不能把所有树的结构在此篇文章中进行详细的介绍,我会通过步步延申的方式去讲解树。
树 ➡ 二叉树➡ 搜索二叉树 ➡ 平衡搜索二叉树 (AVL树和红黑树) ➡ M叉多叉平衡搜索树 (B树和B+树)

1.树的概念及结构

1.1树的定义

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

注意:树形结构中,子树之间不能有交集,否则就不是树形结构。

1.2树的基本概念

我们根据这棵树来介绍一下树的基本概念

  • 节点的度:一个节点含有的子树的个数称为该节点的度,比如说节点1的度为2
  • 叶节点或终端节点:度为0的节点,比如说4,5,6节点
  • 非终端节点或分支节点:度不为0的节点,比如说2,3节点
  • 双亲结点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。比如                                  说2是4,5的父节点
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点,比如说4,5是                                  2的子节点
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点,比如说4,5就是兄弟节点
  • 树的度:一棵树中,最大的节点的度称为树的度,比如说上面这棵树的度为2
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
  • 树的高度或深度:树中节点的最大层次,比如说上面这棵树的高度为3
  • 堂兄弟节点:双亲在同一层的节点互为堂兄弟,比如说5,6节点
  • 节点的祖先:从根到该节点所经分支上的所有节点,比如说1就是所有节点的祖先
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙,比如说所有节点都是1的             子孙
  • 森林:由m(m>0)棵互不相交的树的集合称为森林

1.3 树的表示

我们有三种方法表示上面这棵树:

(1)双亲表示法

双亲表示法是用顺序表,也就是数组对树进行表示的。即用顺序表存储各个节点的数据,并且同时存储其双亲节点的下标。

注意:根节点没有双亲节点,所以特别记为-1。

声明如下:

如下图所示:

存储方式如下:

说明:

  1. 因为根节点没有父节点,将其父节点数组下标设置为-1,根节点存储在顺序表的第1个位置。
  2. 数据元素2、3、4的父节点为0,父节点数组下标为0,分别存储在顺序表的2、3、4个位置。
  3. 数据元素5、6的父节点为1,父节点数组下标为1,分别存储在顺序表的第5、6个位置。
  4. 数据元素7的父节点为3,父节点数组下标为3,存储在顺序表的第7个位置。
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 10
typedef int DataType;
typedef struct TreeNode
{DataType data;int parent;
}Node;typedef struct ParentTree
{// 数据结构的树节点Node Tnode[MAXSIZE];int n;		//当前节点个数
}Ptree;// 初始化树
void TreeInit(Ptree* ptree, int x, int parentIndex) 
{if (ptree->n >= MAXSIZE) {printf("Tree is full!\n");return;}ptree->Tnode[ptree->n].data = x;ptree->Tnode[ptree->n].parent = parentIndex;ptree->n++; // 增加节点计数  
}

双亲表示法的特点:

  • 优点:快速查找一个结点的父结点(直接通过parent字段获取),结构简单,空间开销小(仅需存储值和一个索引)。
  • 缺点:查找子结点时效率低(需遍历整个数组,判断哪些结点的parent等于当前结点的索引),不适合频繁查找子结点的场景。
(2)孩子表示法

树的孩子表示法就是采用顺序表与链表结合的形式,用顺序表存储树的值与链表的头节点,而链表的头节点存储其孩子节点在顺序表中的下标,若没有则记为空(NULL)。

相当于对树进行这样的处理:

存储方式如下:

说明:

添加一个数据(插入一个结点)

  1. 既要在数组中依次添加新的数据。
  2. 也要在其父节点后面用头插法插入。

优缺点:

  • 优点:简单且易于理解,适用于需要频繁查找孩子节点的应用场景。
  • 缺点:不容易直接获取节点的父节点。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>#define MAXSIZE 10
typedef int DataType;
typedef struct ListNode
{int index;struct ListNode* next;
}ListNode;typedef struct TreeNode
{DataType data;ListNode* child;
}TNode;typedef struct Tree
{TNode nodes[MAXSIZE];int n;
}Tree;// 创建一个新的ListNode  
ListNode* createListNode(int index) 
{ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if (newNode == NULL){perror("malloc fail:");exit(0);}newNode->index = index;newNode->next = NULL;return newNode;
}// 初始化树  
void TreeInit(Tree* t, DataType x) 
{if (t->n >= MAXSIZE) {printf("Tree is full!\n");return;}t->nodes[t->n].data = x;t->nodes[t->n].child = NULL;  t->n++;
}// 向节点的子链表中添加一个新节点  
void InsertChild(Tree* t, int parentIndex, int childIndex)
{if (parentIndex >= t->n || childIndex >= t->n) {printf("error\n");return;}ListNode* newNode = createListNode(childIndex);if (newNode == NULL){perror("malloc fail:");return; // 内存分配失败  }newNode->next = t->nodes[parentIndex].child;t->nodes[parentIndex].child = newNode;
}
(3)左孩子右兄弟表示法

最常用表示树的的方法就是左孩子右兄弟表示法,即定义两个指针,让左指针指向子节点,右指针指向兄弟节点。如果没有节点,则都指向空(NULL)。

如图所示:

  • 基本原理:在这种表示法中,每个节点包含一个数据域和两个指针域。其中一个指针指向该节点的第一个孩子节点(左孩子),另一个指针指向该节点的下一个兄弟节点(右兄弟)。通过这种方式,可将任意一棵多叉树转化为一棵二叉树来表示。

节点定义:在 C++ 中,可按如下方式定义节点结构:

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;typedef struct TreeNode
{DataType data;struct TreeNode* leftChild;  // 指向左孩子  struct TreeNode* rightBro; // 指向右兄弟  
} TNode;// 创建一个新的树节点
TNode* createTreeNode(DataType x)
{TNode* newNode = (TNode*)malloc(sizeof(TNode));if (newNode == NULL){perror("malloc fail:");exit(0);}newNode->data = x;newNode->leftChild = NULL;newNode->rightBro = NULL;return newNode;
}void InsertChild(TNode* t, DataType x, int isLeftChild) 
{if (t == NULL) {printf("Cannot insert into NULL node\n");return;}TNode* newNode = createTreeNode(x);if (newNode == NULL) {return;}// 如果isLeftChild为1,表示新节点应该作为左孩子插入  if (isLeftChild) {// 如果当前节点还没有右兄弟,则直接将新节点设置为右兄弟if (t->leftChild == NULL) {t->leftChild = newNode;}// 如果当前节点已经有左孩子,则遍历左孩子的右兄弟链表// 找到最后一个右兄弟,并将新节点插入为它的下一个右兄弟else {TNode* tmp = t->leftChild;while (tmp->rightBro != NULL) {tmp = tmp->rightBro;}tmp->rightBro = newNode;}}// 如果isLeftChild为0,表示新节点应该作为右兄弟插入else{// 如果当前节点还没有右兄弟,则直接将新节点设置为右兄弟if (t->rightBro == NULL) {t->rightBro = newNode;}// 如果当前节点已经有右兄弟,则遍历右兄弟链表// 找到最后一个右兄弟,并将新节点插入为它的下一个右兄弟else {TNode* tmp = t->rightBro;while (tmp->rightBro != NULL) {tmp = tmp->rightBro;}tmp->rightBro = newNode;}}
}

1.4 树在实际中的运用(表示文件系统的目录树结构)

在linux环境下目录结构就是有一颗树构成,而在Windows环境下,目录许多内容并不交叉,所以是由森林构成。

 树的性质(重点)

 性质1:树中的节点数等于所有节点度数加 1
        从上图可知,有 12 个节点  ,A的度为:3, B的度为2, C 的度为1, D的度为2, E的度为1, F的度为0, G的度为2, H的度为0 , I的度为0,J的度为0,K的度0,L的度为0。


        由性质 1 可知 树的节点个数 = 每个节点的度数 + 1 

所以上图 树的节点数 = (3+2+1+2+1+0+2+0+0+0+0+0)+1 = 12
 

性质2:度为 m 的树中第 i 层至多有 m^i-1 个节点(i>=1)

性质3:高度为 h 的 m 叉树至多有  m^h-1/m-1 个节点

性质4:具有n个结点的m叉树的最小高度为⌈logm(n*(m-1)+1)⌉

结束语:

下一节我们将继续深入学习树的知识

感谢你的三连支持!!!

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

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

相关文章

告别双系统——WSL2+UBUNTU在WIN上畅游LINUX

在Windows 11上配置WSL开发环境指南 最近换工作需要深入研究代码&#xff0c;发现WSL&#xff08;Windows Subsystem for Linux&#xff09;是微软为Windows开发者提供的强大工具&#xff0c;可以在Windows上直接运行Ubuntu子系统&#xff0c;无需双系统或虚拟机&#xff08;满…

Python爬虫实战:研究Ticks and spines模块,构建电商数据采集和分析系统

1. 引言 1.1 研究背景 在信息时代,互联网数据呈现爆炸式增长,涵盖社会、经济、文化等多个领域,具有极高的研究与应用价值。如何高效获取目标数据并进行深度分析,成为信息处理领域的重要课题。Python 凭借其丰富的库支持和简洁的语法,在数据爬取与分析领域得到广泛应用:…

前端基础 —— B / CSS基础

一、CSS 基础概述定义&#xff1a;层叠样式表&#xff08;Cascading Style Sheets&#xff09;作用&#xff1a;美化页面、实现样式与结构分离二、CSS 基本语法与引入方式1. 语法规范选择器 {一条/N条声明}选择器决定针对谁修改 (找谁) 声明决定修改啥. (干啥)<style> p…

智能农机无人驾驶作业套圈路径规划

国产轻量级桌面GIS软件Snaplayers实践&#xff1a;智能农机无人驾驶作业套圈路径规划1、选择地块角点坐标文件2、加载地块到地图中3、设置套圈作业路径规划参数4、生成套圈作业路径5、查看套圈路径6、查看套圈路径8、完成本算法已经在国内外等农场已经使用多年。Snaplayers研发…

Java Collection集合框架:体系、核心与选型

目录 一、集合框架的顶层设计&#xff1a;接口与层次 1. 两大核心接口&#xff1a;Collection 和 Map 2. Collection接口的三大派系 二、核心实现类详解 1. List家族实现 2. Set家族实现 3. Queue/Deque家族实现 PriorityQueue&#xff1a; ArrayDeque&#xff1a; 三…

“计算机基础、软件工程、设计模式、数据结构算法、操作系统、数据库、网络、法律法规”是计算机领域从基础理论到工程实践

“计算机基础、软件工程、设计模式、数据结构算法、操作系统、数据库、网络、法律法规”是计算机领域从基础理论到工程实践、再到合规规范的核心知识体系&#xff0c;覆盖了软件开发、系统架构、技术合规等关键维度。以下将对每个领域进行系统拆解&#xff0c;包括核心内容、学…

利用Rancher平台搭建Swarm集群

一、Rancher概述1、rancher平台Rancher是一个开源的企业级容器管理平台&#xff0c;它可以帮助企业在生产环境中轻松快捷地部署和管理容器&#xff0c;也可以轻松管理各种环境的Kubernetes&#xff0c;并提供对DevOps的支持。Rancher目前已经具备全栈化一键部署应用、各种编排调…

Debezium日常分享系列之:MongoDB 新文档状态提取

Debezium日常分享系列之&#xff1a;MongoDB 新文档状态提取变更事件结构行为配置数组编码嵌套结构展平MongoDB $unset 处理确定原始操作添加元数据字段选择性应用转换的选项配置选项已知限制Debezium MongoDB 连接器会发出数据变更消息&#xff0c;以表示 MongoDB 集合中发生的…

OpenCV:图像透视变换

文章目录一、透视变换是什么&#xff1f;二、透视变换的核心原理1. 关键概念&#xff1a;透视变换矩阵2. 核心条件&#xff1a;4对对应点三、OpenCV实现透视变换的关键步骤步骤1&#xff1a;读取并预处理图像步骤2&#xff1a;寻找目标物体的4个顶点步骤3&#xff1a;计算透视变…

commons-csv

maven依赖<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-csv --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.14.1</version></dependency…

LeetCode 1446.连续字符

给你一个字符串 s &#xff0c;字符串的「能量」定义为&#xff1a;只包含一种字符的最长非空子字符串的长度。 请你返回字符串 s 的 能量。 示例 1&#xff1a; 输入&#xff1a;s “leetcode” 输出&#xff1a;2 解释&#xff1a;子字符串 “ee” 长度为 2 &#xff0c;只包…

CTFHub SSRF通关笔记9:302跳转 Bypass 原理详解与渗透实战

目录 一、SSRF与302跳转 1、SSRF 2、302响应 3、SSRF与302结合 &#xff08;1&#xff09;SSRF源码分析 &#xff08;2&#xff09;攻击链条&#xff08;Flow of Exploit&#xff09; 二、渗透实战 1、打开靶场 2、尝试127.0.0.1访问 3、file协议分析源码 &#xff…

Windows-Use实战:AI驱动的Windows自动化

Windows-Use实战:AI驱动的Windows自动化 前言 项目介绍与准备工作 Windows-Use是什么? 系统要求 必需环境 步骤一:安装Python和基础环境 1.1 安装Python 检查Python版本 Python安装步骤 1.2 创建项目目录 步骤二:安装Windows-Use 2.1 使用pip安装(推荐) 步骤三:运行和基…

STM32-FreeRTOS操作系统-二值信号量与计数信号量

引言在嵌入式开发领域&#xff0c;任务同步与通信是系统稳定运行的核心。STM32配合FreeRTOS操作系统&#xff0c;为开发者提供了强大的工具支持。其中&#xff0c;二值信号量和计数信号量作为FreeRTOS的关键同步机制&#xff0c;分别用于任务间的简单同步和资源计数控制。二值信…

MarTech营销技术全景解析:概念、图谱与最新实践案例

一、引言&#xff1a;为什么企业越来越依赖MarTech&#xff1f;在数字化浪潮下&#xff0c;企业营销环境正发生深刻变化&#xff1a;客户触点增加&#xff1a;从官网、社交媒体到短视频、展会&#xff0c;信息渠道呈指数级增长。决策链条复杂&#xff1a;B2B客户通常需要多轮调…

服务器 - 从一台服务器切换至另一台服务器(损失数十条访客记录)

服务器 - 从一台服务器切换至另一台服务器(损失数十条PV记录为代价) 看着四年的服务器正式到期&#xff0c;没什么轰轰烈烈的告别&#xff0c;就像目送老朋友转身走远&#xff0c;只默默记下&#xff1a;哦&#xff0c;原来它陪了我这么久啊。 前言 一台陪伴了我4年的服务器昨…

《云原生边缘与AI训练场景:2类高频隐蔽Bug的深度排查与架构修复》

在云原生技术向边缘计算与AI训练场景渗透的过程中&#xff0c;基础设施层的问题往往会被场景特性放大——边缘环境的弱网络、异构硬件&#xff0c;AI训练的高资源依赖、分布式协作&#xff0c;都可能让原本隐藏的Bug以“业务故障”的形式爆发。这些问题大多不具备直观的报错信息…

【51单片机】【protues仿真】基于51单片机数控直流稳压电源系统

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 一、主要功能 1、数码管显示输出电压值 2、滑动电阻调节输出电压 3、电压输出范围0-15V&#xff0c;步进值1 二、使用步骤 基于51单片机的数控直流稳压电源是一种通过数字控制实现电压调节的智…

xtuoj Rectangle

题目思路将矩形间的相交情况通过投影转化为x、y两个方向下的线段是否相交&#xff0c;即前面的题目&#xff0c;判断两个区间是否相交&#xff0c;x投影的每个区间的左端点是每个矩形x的min&#xff0c;右端点是每个矩形的x的max&#xff0c;y投影情况同理&#xff0c;只要x轴的…

【深度学习踩坑实录】从 Checkpoint 报错到 TrainingArguments 精通:QNLI 任务微调全流程复盘

作为一名深度学习初学者&#xff0c;最近在基于 Hugging Face Transformers 微调 BERT 模型做 QNLI 任务时&#xff0c;被Checkpoint 保存和TrainingArguments 配置这两个知识点卡了整整两天。从磁盘爆满、权重文件加载报错&#xff0c;到不知道如何控制 Checkpoint 数量&#…