Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
Python系列文章目录
Go语言系列文章目录
Docker系列文章目录
数据结构与算法系列文章目录
01-【数据结构与算法-Day 1】程序世界的基石:到底什么是数据结构与算法?
02-【数据结构与算法-Day 2】衡量代码的标尺:时间复杂度与大O表示法入门
03-【数据结构与算法-Day 3】揭秘算法效率的真相:全面解析O(n^2), O(2^n)及最好/最坏/平均复杂度
04-【数据结构与算法-Day 4】从O(1)到O(n²),全面掌握空间复杂度分析
05-【数据结构与算法-Day 5】实战演练:轻松看懂代码的时间与空间复杂度
06-【数据结构与算法-Day 6】最朴素的容器 - 数组(Array)深度解析
07-【数据结构与算法-Day 7】告别数组束缚,初识灵活的链表 (Linked List)
08-【数据结构与算法-Day 8】手把手带你拿捏单向链表:增、删、改核心操作详解
09-【数据结构与算法-Day 9】图解单向链表:从基础遍历到面试必考的链表反转
10-【数据结构与算法-Day 10】双向奔赴:深入解析双向链表(含图解与代码)
11-【数据结构与算法-Day 11】从循环链表到约瑟夫环,一文搞定链表的终极形态
12-【数据结构与算法-Day 12】深入浅出栈:从“后进先出”原理到数组与链表双实现
13-【数据结构与算法-Day 13】栈的应用:从括号匹配到逆波兰表达式求值,面试高频考点全解析
14-【数据结构与算法-Day 14】先进先出的公平:深入解析队列(Queue)的核心原理与数组实现
15-【数据结构与算法-Day 15】告别“假溢出”:深入解析循环队列与双端队列
16-【数据结构与算法-Day 16】队列的应用:广度优先搜索(BFS)的基石与迷宫寻路实战
17-【数据结构与算法-Day 17】揭秘哈希表:O(1)查找速度背后的魔法
18-【数据结构与算法-Day 18】面试必考!一文彻底搞懂哈希冲突四大解决方案:开放寻址、拉链法、再哈希
19-【数据结构与算法-Day 19】告别线性世界,一文掌握树(Tree)的核心概念与表示法
文章目录
摘要
在数据结构的世界里,我们已经探索了数组、链表、栈和队列等线性结构,它们如同串成一线的珍珠,元素之间维持着简单的一对一关系。然而,现实世界充满了复杂的层级关系,例如公司组织架构、文件系统目录、家族谱系等。为了高效地模拟这些“一对多”的关系,一种全新的、功能强大的非线性结构——**树(Tree)**应运而生。本文将作为您踏入非线性结构世界的第一站,系统地为您讲解树的基本概念,从最基础的节点、边,到描述其特性的深度、高度、度,并深入探讨在计算机中表示树的两种主流方法。掌握这些基础,是深入学习二叉树、平衡树、堆等高级数据结构不可或缺的基石。
一、为什么需要树?—— 从线性到非线性
在深入树的细节之前,我们首先要理解,为什么我们需要从熟悉的线性结构迈向非线性结构。
1.1 线性结构的局限性
线性数据结构,如数组和链表,其核心特征是数据元素之间存在“一对一”的线性关系。每个元素(除了首尾)都有一个唯一的前驱和一个唯一的后继。这种结构非常适合表示顺序序列,比如排队的人群、文章的段落。
然而,当我们需要表示以下关系时,线性结构就显得力不从心了:
- 层级关系:公司的组织架构中,一个经理可以管理多个员工。
- 分类关系:图书分类中,一个大类(如“计算机科学”)下可以有多个子类(如“编程语言”、“数据库”)。
- 从属关系:电脑的文件系统中,一个文件夹可以包含多个文件和其他文件夹。
用线性结构来强行表示这些关系,会变得异常复杂和低效。
1.2 树:描述层级关系的神器
为了解决上述问题,**树(Tree)**结构应运而生。树是一种抽象数据类型(ADT),它由 n(n>=0)个有限节点组成一个具有层次关系的集合。当 n=0 时,我们称之为空树。
树结构能够完美地模拟现实世界中的层级关系,它直观、高效,是我们工具箱中不可或缺的利器。
下面是一个典型的树形结构——公司组织架构图:
这张图清晰地展示了“一对多”的隶属关系,这就是树的威力所在。
二、深入浅出:树(Tree)的核心术语
为了能够准确地讨论和使用树,我们需要学习一套标准的“行话”。下面,我们将通过一个示例图来解释树的所有核心术语。
示例树:
2.1 基础构成元素
2.1.1 节点 (Node)
节点是树的基本组成单元。在图中,A, B, C, D, E, F, G, H, I 都是节点。一个节点通常包含两个部分:
- 数据域 (Data):存储节点的具体信息,如员工姓名、文件夹名称等。
- 指针域 (Pointers):存储指向其子节点的链接。
2.1.2 边 (Edge)
边是连接两个节点之间的线,代表它们之间的直接关系。例如,从 A 到 B 的箭头就是一条边。
2.2 节点间的关系与角色
2.2.1 根节点 (Root)
一棵树有且仅有一个没有父节点的节点,我们称之为根节点。它是树的入口。
- 示例:节点
A是这棵树的根节点。
2.2.2 父节点 (Parent) 与 子节点 (Child)
如果一个节点 X 连接到另一个节点 Y,那么 X 称为 Y 的父节点,Y 称为 X 的子节点。
- 示例:
A是B、C、D的父节点;B、C、D是A的子节点。
2.2.3 兄弟节点 (Sibling)
拥有相同父节点的节点互称为兄弟节点。
- 示例:
B、C、D互为兄弟节点。H和I互为兄弟节点。
2.2.4 叶子节点 (Leaf / Terminal Node)
没有任何子节点的节点称为叶子节点或终端节点。
- 示例:
D,E,G,H,I都是叶子节点。
2.2.5 内部节点 (Internal / Non-terminal Node)
除了根节点和叶子节点之外的所有节点,即至少有一个子节点的非根节点,称为内部节点。有时也宽泛地指所有非叶子节点。
- 示例:
B,C,F是内部节点。A也是一个非叶子节点。
2.3 树的整体特性
2.3.1 节点的度 (Degree)
一个节点拥有的子树(或子节点)的数量称为该节点的度。树中所有节点度的最大值称为树的度。
- 示例:
- 节点
A的度为 3。 - 节点
F的度为 2。 - 叶子节点
G的度为 0。 - 这棵树的度为 3(取所有节点度的最大值)。
- 节点
2.3.2 节点的层次 (Level)
从根节点开始定义,根在第 1 层,根的子节点在第 2 层,以此类推。
- 示例:
- Level 1:
A - Level 2:
B,C,D - Level 3:
E,F,G - Level 4:
H,I
- Level 1:
注意:在某些教材或定义中,根节点的层次可能从 0 开始。这是一种约定俗成的差异,理解其相对性即可,在具体问题中保持一致性是关键。
2.3.3 树的深度 (Depth)
树的深度是指树中节点的最大层次。它描述了树的“纵向”有多深。
- 示例:这棵树的最大层次是 4,所以树的深度为 4。
关于节点的深度:从根节点到该节点所经过的边的数量。根节点的深度为 0,其子节点的深度为 1,以此类推。一棵树的深度等于其最深叶子节点的深度。
2.3.4 树的高度 (Height)
树的高度是指树中节点的最大高度。
- 示例:这棵树的高度为 3。
关于节点的高度:从该节点到其最远叶子节点所经过的边的数量。叶子节点的高度为 0,其父节点的高度是其所有子节点高度最大值加 1。一棵树的高度等于其根节点的高度。
(1)深度与高度的辨析
深度和高度是两个容易混淆但至关重要的概念。
- 深度 (Depth):是“自上而下”看的,从根节点出发。
- 高度 (Height):是“自下而上”看的,从叶子节点出发。
| 概念 | 参考系 | 方向 |
|---|---|---|
| 深度 | 根节点 | 从根到当前节点 |
| 高度 | 叶子节点 | 从当前节点到最远叶子 |
对于整棵树而言,树的高度等于树的深度。但对于任意一个节点,其高度和深度通常是不同的。
2.4 特殊概念
2.4.1 子树 (Subtree)
树中任意一个节点和它所有的后代(子孙)节点组成的集合,也构成一棵树,称为原树的子树。
- 示例:以节点
B为根的B-E-F-H-I结构就是A树的一棵子树。
2.4.2 森林 (Forest)
m (m>=0) 棵互不相交的树的集合称为森林。把一棵树的根节点删除,其所有的子树就构成了一个森林。
- 示例:如果我们删除根节点
A,那么以B、C、D为根的三棵树就组成了一个森林。
三、树的计算机表示法
理解了树的理论概念后,我们如何在代码中把它“造”出来呢?主要有两种方法:数组表示法和链式表示法。
3.1 数组表示法 (父节点表示法)
这种方法使用一个一维数组来存储树的各个节点。数组的索引(下标)可以唯一标识一个节点,而数组元素的值则存储该节点的父节点在数组中的索引。根节点没有父节点,通常用一个特殊值(如 -1)表示。
示例:
我们将上面的示例树(稍作简化,假设节点用数字 0-8 表示 A-I)用父节点表示法存储:
节点: A B C D E F G H I
索引: 0 1 2 3 4 5 6 7 8
父索引:-1 0 0 0 1 1 2 5 5
| 数组索引 (节点) | 0 (A) | 1 (B) | 2 © | 3 (D) | 4 (E) | 5 (F) | 6 (G) | 7 (H) | 8 (I) |
|---|---|---|---|---|---|---|---|---|---|
| 父节点索引 | -1 | 0 | 0 | 0 | 1 | 1 | 2 | 5 | 5 |
| 数据 | dataA | dataB | dataC | dataD | dataE | dataF | dataG | dataH | dataI |
优点:
- 实现简单,易于理解。
- 查找任意节点的父节点非常快,时间复杂度为 O ( 1 ) O(1) O(1)。
缺点:
- 查找任意节点的子节点很慢,需要遍历整个数组,时间复杂度为 O ( n ) O(n) O(n)。
- 如果树的结构频繁变动(增加、删除节点),维护数组会非常麻烦。
- 可能造成空间浪费。
3.2 链式表示法
链式表示法克服了数组表示法的一些缺点,是更常用和灵活的方法。其中,孩子兄弟表示法是最高效和通用的链式表示方法。
3.2.1 孩子兄弟表示法 (Child-Sibling Representation)
这种表示法的核心思想是:每个节点包含两个指针(或引用):
- 一个指针指向它的第一个孩子节点 (first child)。
- 另一个指针指向它的下一个兄弟节点 (next sibling)。
通过这种巧妙的设计,任意度的树都可以用这种结构来表示,每个节点只需要固定数量的指针。
(1)代码实现
下面是孩子兄弟表示法的节点结构定义示例(以 Python 为例):
class TreeNode:
"""
树节点类(孩子兄弟表示法)
"""
def __init__(self, value):
self.value = value # 节点存储的数据
self.first_child = None # 指向第一个孩子的引用
self.next_sibling = None # 指向下一个兄弟的引用
# 构建示例树的一部分
root = TreeNode('A')
b = TreeNode('B')
c = TreeNode('C')
d = TreeNode('D')
# A的孩子是B, C, D
root.first_child = b
b.next_sibling = c
c.next_sibling = d
e = TreeNode('E')
f = TreeNode('F')
# B的孩子是E, F
b.first_child = e
e.next_sibling = f
print(f"根节点 {root.value} 的第一个孩子是 {root.first_child.value}")
print(f"节点 {b.value} 的下一个兄弟是 {b.next_sibling.value}")
3.3 表示法对比总结
| 特性 | 父节点表示法 (数组) | 孩子兄弟表示法 (链式) |
|---|---|---|
| 查找父节点 | 极快 ( O ( 1 ) O(1) O(1)) | 较慢,需要遍历 (最坏 O ( n ) O(n) O(n)) |
| 查找子节点 | 慢,需遍历整个数组 ( O ( n ) O(n) O(n)) | 较快,遍历孩子链表 |
| 查找兄弟节点 | 慢,需遍历整个数组 ( O ( n ) O(n) O(n)) | 快,直接访问 next_sibling (
O
(
1
)
O(1)
O(1)) |
| 增删节点 | 困难,可能需要移动大量元素 | 灵活,只需修改指针 |
| 空间复杂度 | 固定为 O ( n ) O(n) O(n) | O ( n ) O(n) O(n),但节点对象开销稍大 |
| 适用场景 | 静态树,主要关心父子关系 | 动态树,需要频繁操作和遍历 |
在绝大多数应用场景中,孩子兄弟表示法因其灵活性和高效的遍历能力而成为首选。
四、总结
恭喜你,成功开启了非线性数据结构的大门!今天我们系统地学习了树的基础知识,现在让我们回顾一下核心要点:
-
树的价值:树结构是为表示“一对多”的层级关系而生的,它弥补了线性结构在这方面的不足,是计算机科学中模拟现实世界层级模型的基础。
-
核心术语:我们掌握了一套描述树的通用语言,包括节点、边、根、父、子、兄弟、叶子节点等基本角色,以及度、层次、深度、高度等衡量树形态的关键指标。请务必区分深度(自上而下)和高度(自下而上)。
-
存储实现:我们探讨了两种主要的计算机表示法。**父节点表示法(数组)简单但操作受限,适用于静态场景。而孩子兄弟表示法(链式)**通过巧妙的
firstChild和nextSibling指针,实现了对任意树的灵活、高效存储,是动态树结构的首选方案。
打好这个坚实的基础后,我们下一篇文章将聚焦于一种最重要、最常见的树——二叉树,敬请期待!
的核心概念与表示法&spm=1001.2101.3001.5002&articleId=150870057&d=1&t=3&u=08413e13fc3a4f5cbd607f3af9ff6915)
416

被折叠的 条评论
为什么被折叠?



