当前位置: 首页 > news >正文

【数据结构与算法-Day 20】从零到一掌握二叉树:定义、性质、特殊形态与存储结构全解析

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)的核心概念与表示法
20-【数据结构与算法-Day 20】从零到一掌握二叉树:定义、性质、特殊形态与存储结构全解析


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • Python系列文章目录
  • Go语言系列文章目录
  • Docker系列文章目录
  • 数据结构与算法系列文章目录
  • 摘要
  • 一、告别线性,拥抱层级:再识树结构
    • 1.1 为什么需要树?
    • 1.2 树的核心术语回顾
  • 二、二叉树:结构最简、应用最广的树
    • 2.1 什么是二叉树?
      • 2.1.1 严格定义
      • 2.1.2 “左右”之分的重要性
    • 2.2 二叉树的三大基本性质
      • 2.2.1 性质 1:层级与节点数上限
      • 2.2.2 性质 2:深度与总节点数上限
      • 2.2.3 性质 3:叶子节点与度为2节点的关系
    • 2.3 二叉树 vs 普通树
  • 三、二叉树的特殊形态:满二叉树与完全二叉树
    • 3.1 完美主义者:满二叉树
      • 3.1.1 定义与图示
    • 3.2 实用主义者:完全二叉树
      • 3.2.1 定义与图示
      • 3.2.2 完全二叉树的重要性质
      • 3.2.3 为何完全二叉树如此重要?
    • 3.3 辨析:满二叉树 vs 完全二叉树
  • 四、二叉树的存储结构
    • 4.1 链式存储法 (Linked Representation)
      • 4.1.1 节点结构定义
      • 4.1.2 图解链式结构
      • 4.1.3 优缺点分析
    • 4.2 顺序存储法(数组表示法)
      • 4.2.1 实现原理
      • 4.2.2 图解数组存储
      • 4.2.3 适用场景与优缺点
    • 4.3 存储方式的选择
  • 五、总结


摘要

在数据结构的宏伟蓝图中,如果说数组和链表是构建线性世界的基石,那么“树”则是开启非线性、层级化世界大门的钥匙。而在形态各异的树结构家族中,二叉树(Binary Tree) 无疑是血统最纯正、应用最广泛的“王者”。它以其简洁的定义、优美的性质和高效的实现,成为了无数复杂数据结构(如二叉搜索树、堆、红黑树)的根基。本文将带领您从零开始,系统地学习二叉树的定义、核心性质、两种重要的特殊形态(满二叉树与完全二叉树),并深入探讨其在计算机中的两种主流存储方式。掌握二叉树,是您从线性思维迈向非线性思维的关键一步。


一、告别线性,拥抱层级:再识树结构

在进入二叉树的世界之前,让我们简要回顾为何需要树这种结构。

1.1 为什么需要树?

我们已经熟悉的数组和链表,都属于线性数据结构。它们擅长表示前后相继、一一对应的关系,就像排队的人群。然而,现实世界充满了更复杂的层级关系,例如:

  • 公司的组织架构(董事长 -> CEO -> 部门总监 -> 员工)
  • 计算机的文件系统(根目录 -> 子目录 -> 文件)
  • 书籍的章节目录

用线性结构来描述这些关系会非常笨拙和低效。为此,树(Tree) 这种非线性数据结构应运而生,它能完美地模拟这种“一对多”的层级关系。

1.2 树的核心术语回顾

在上一篇文章中,我们介绍了树的基本概念。这里我们用一张图快速回顾一下:

Legend
节点 Node
边 Edge
根 Root: A
父 Parent: B是E的父
子 Child: E是B的子
兄弟 Sibling: B,C,D
叶子 Leaf: E,F,G,D
度 Degree: A的度为3
高度 Height: 3
根节点 A
子节点 B
子节点 C
子节点 D
叶子节点 E
叶子节点 F
叶子节点 G

有了这些基础,我们就可以正式请出今天的主角——二叉树。

二、二叉树:结构最简、应用最广的树

2.1 什么是二叉树?

2.1.1 严格定义

二叉树(Binary Tree) 是一个有限的节点集合,这个集合或者为空,或者由一个根节点和两棵互不相交的、分别称为根节点的**左子树(Left Subtree)右子树(Right Subtree)**的二叉树组成。

从定义中,我们可以提炼出几个关键点:

  1. 可以为空:一棵二叉树可以没有任何节点。
  2. 最多两个孩子:每个节点最多只能有两个子节点,即节点的度(Degree)不能超过 2。
  3. 有序性:子树是分左右的,次序不能颠倒。一个节点的左子树和右子树是不同的概念,即使它们结构完全一样。

2.1.2 “左右”之分的重要性

“有序性”是二叉树与普通树一个非常关键的区别。在普通树中,一个节点的子节点之间没有固定的顺序。但在二叉树中,左右位置是固定的。

情况二:只有右孩子
情况一:只有左孩子
C
A
B
A

上图中的两棵树,在普通树的视角下可能被视为相同(A有一个孩子),但在二叉树中,它们是完全不同的两棵树。

2.2 二叉树的三大基本性质

任何一棵二叉树,无论其形态如何,都满足以下几个基本性质,这些性质在算法分析中非常有用。

2.2.1 性质 1:层级与节点数上限

性质 1:在二叉树的第 iii 层上,至多有 2i−12^{i-1}2i1 个节点(i≥1i \ge 1i1)。

  • 理解:第 1 层是根节点,最多 1 个 (21−1=12^{1-1}=1211=1);第 2 层是根的孩子,最多 2 个 (22−1=22^{2-1}=2221=2);第 3 层的节点是第 2 层节点的孩子,最多 4 个 (23−1=42^{3-1}=4231=4),以此类推。

2.2.2 性质 2:深度与总节点数上限

性质 2:深度为 kkk 的二叉树,至多有 2k−12^k - 12k1 个节点(k≥1k \ge 1k1)。

  • 理解:这是性质 1 的推论。将每一层的最大节点数相加,构成一个等比数列求和:20+21+22+...+2k−1=1(2k−1)2−1=2k−12^0 + 2^1 + 2^2 + ... + 2^{k-1} = \frac{1(2^k-1)}{2-1} = 2^k - 120+21+22+...+2k1=211(2k1)=2k1

2.2.3 性质 3:叶子节点与度为2节点的关系

性质 3:对任何一棵二叉树,如果其叶子节点(度为0的节点)数为 n0n_0n0,度为2的节点数为 n2n_2n2,则 n0=n2+1n_0 = n_2 + 1n0=n2+1

  • 理解:这是一个非常有趣的结论。我们可以从“边”的数量来思考。
    • 设树的总节点数为 NNN,度为1的节点数为 n1n_1n1。则 N=n0+n1+n2N = n_0 + n_1 + n_2N=n0+n1+n2
    • 树中总边数 EEE 等于除了根节点外,每个节点都有一个指向它的入边,所以 E=N−1E = N - 1E=N1
    • 从另一个角度看,总边数等于所有节点的度之和。E=(0×n0)+(1×n1)+(2×n2)=n1+2n2E = (0 \times n_0) + (1 \times n_1) + (2 \times n_2) = n_1 + 2n_2E=(0×n0)+(1×n1)+(2×n2)=n1+2n2
    • 联立两个方程:N−1=n1+2n2N - 1 = n_1 + 2n_2N1=n1+2n2
    • N=n0+n1+n2N = n_0 + n_1 + n_2N=n0+n1+n2 代入,得到 (n0+n1+n2)−1=n1+2n2(n_0 + n_1 + n_2) - 1 = n_1 + 2n_2(n0+n1+n2)1=n1+2n2
    • 化简后即可得到:n0=n2+1n_0 = n_2 + 1n0=n2+1

2.3 二叉树 vs 普通树

为了加深理解,我们用一个表格来对比二叉树和我们一般意义上说的“树”。

特性普通树 (General Tree)二叉树 (Binary Tree)
节点度任意,可以大于 2最大为 2
子节点顺序无序,不区分孩子们的次序有序,严格区分左、右子树
基本单位节点节点(包含左、右指针域)
核心关注点节点的父子关系节点的左右子树结构

三、二叉树的特殊形态:满二叉树与完全二叉树

在二叉树家族中,有两个“模范生”因其规整的结构而备受关注。

3.1 完美主义者:满二叉树

3.1.1 定义与图示

满二叉树(Full Binary Tree):一棵深度为 kkk 且有 2k−12^k - 12k1 个节点的二叉树。

通俗地讲,一棵满二叉树的每一层都“塞满”了节点,所有叶子节点都必须在最下层,且所有非叶子节点的度都为2。它像一个完美的等边三角形。

一棵深度为3的满二叉树
3
1
2
4
5
6
7

3.2 实用主义者:完全二叉树

满二叉树的条件过于苛刻,现实中很少见。相比之下,完全二叉树则更为常见和实用。

3.2.1 定义与图示

完全二叉树(Complete Binary Tree):设一棵深度为 kkk 的二叉树,其从第 1 层到第 k−1k-1k1 层都是满的,第 kkk 层的节点从左到右是连续的。

可以想象成给满二叉树的节点从上到下、从左到右依次编号,完全二叉树就是编号从 1 到 n 连续的那些节点构成的树。

❌ 不是完全二叉树
✅ 是完全二叉树
2
1
3
4
6
7
节点5的位置空缺了
2
1
3
4
5
6

3.2.2 完全二叉树的重要性质

完全二叉树的规整性带来了两条至关重要的性质,这两条性质是**堆(Heap)**这种数据结构能够用数组高效实现的基础。

性质 4:具有 nnn 个节点的完全二叉树的深度为 ⌊log⁡2n⌋+1\lfloor \log_2 n \rfloor + 1log2n+1

  • 理解:这是节点数和深度之间最紧凑的关系,因为节点都是挨着排列的。

性质 5:如果对一棵有 nnn 个节点的完全二叉树按层序编号(从1开始),则对任一节点 iii1≤i≤n1 \le i \le n1in)有:

  1. i>1i > 1i>1,其父节点(Parent)的编号为 ⌊i/2⌋\lfloor i/2 \rfloori/2
  2. 2i≤n2i \le n2in,其左孩子(Left Child)的编号为 2i2i2i;否则无左孩子。
  3. 2i+1≤n2i+1 \le n2i+1n,其右孩子(Right Child)的编号为 2i+12i+12i+1;否则无右孩子。

3.2.3 为何完全二叉树如此重要?

答案是:性质 5!这个性质揭示了在完全二叉树中,节点的物理存储位置(数组下标)与其在逻辑树结构中的父子关系之间存在着简单的数学换算。这意味着,我们不需要使用指针,仅通过数组下标的计算就能找到任意节点的父、子节点。这使得用数组来存储完全二叉树变得异常高效,我们将在下一节详细探讨。

3.3 辨析:满二叉树 vs 完全二叉树

  • 关系:满二叉树是一种特殊的、更完美的完全二叉树。
  • 区别:完全二叉树不要求所有叶子节点都在同一层,最后一层可以不满,但节点必须靠左排列。

四、二叉树的存储结构

理论讲完,我们来看看在代码中如何表示一棵二叉树。

4.1 链式存储法 (Linked Representation)

这是最直观、最常用的方法,类似于链表的实现方式。

4.1.1 节点结构定义

我们定义一个节点类(或结构体),它包含三部分:数据域、指向左孩子的指针和指向右孩子的指针。

// Java 代码示例:定义二叉树节点
public class TreeNode {public int value;          // 节点存储的数据public TreeNode left;      // 指向左子树的引用public TreeNode right;     // 指向右子树的引用public TreeNode(int value) {this.value = value;this.left = null;this.right = null;}
}

4.1.2 图解链式结构

一棵逻辑上的二叉树,在内存中通过引用(指针)连接起来,形成如下结构:

value: 10
left: B
right: C
value: 5
left: null
right: D
value: 15
left: null
right: null
value: 7
left: null
right: null

图示:节点 A 的 left 字段存储了节点 B 的内存地址,right 字段存储了节点 C 的内存地址。

4.1.3 优缺点分析

  • 优点
    • 灵活:可以表示任意形状的二叉树。
    • 按需分配:内存空间仅与节点数量成正比,不会浪费。
  • 缺点
    • 空间开销:每个节点都需要额外的空间存储两个指针。
    • 查找父节点困难:从一个子节点出发,无法直接找到其父节点,除非在节点定义中增加一个父指针。

4.2 顺序存储法(数组表示法)

这种方法主要利用了前面提到的完全二叉树的性质 5

4.2.1 实现原理

将二叉树的节点按照层序遍历的顺序存入一个数组中。根节点存放在索引 0 或 1 的位置(以索引 0 为例,父子关系变为:parent = (i-1)/2, left = 2i+1, right = 2i+2)。如果某个位置的节点不存在,则在数组中用一个特殊值(如 null-1)来表示。

4.2.2 图解数组存储

对于下面这棵完全二叉树:

A
B
C
D
E
F

它的数组表示为:

索引012345
ABCDEF

如果树不是完全二叉树,比如缺少了节点 E:

A
B
C
D
F

其数组表示将出现“空洞”:

索引012345
ABCDnullF

4.2.3 适用场景与优缺点

  • 优点
    • 高效寻址:通过下标计算即可快速定位父子节点,无需指针。
    • 节省空间(对完全二叉树而言):无需存储指针,结构紧凑。
  • 缺点
    • 空间浪费:对于非完全二叉树,特别是“倾斜”的树(Skewed Tree),会浪费大量数组空间。例如,一个只有右孩子的链状树,会造成指数级的空间浪费。
    • 不灵活:数组大小固定,插入和删除节点可能涉及大量元素移动。

4.3 存储方式的选择

对比维度链式存储 (Linked)顺序存储 (Array)
适用树形任意形状的二叉树完全二叉树或接近完全二叉树
内存使用节点数 * (数据大小 + 2*指针大小)严重依赖树的形态,最坏情况可能极大
查找父节点困难(O(n)),除非加父指针极快(O(1)),通过下标计算
查找子节点极快(O(1)),通过指针访问极快(O(1)),通过下标计算
插入/删除相对容易,修改指针即可困难,可能涉及数组扩容和元素移动

核心思想:当你的应用场景能保证树的形态始终是完全二叉树时(如),顺序存储是最佳选择。在其他大多数情况下,链式存储是更通用、更安全的选择。

五、总结

今天我们系统地学习了二叉树这一至关重要的数据结构,现在我们来梳理一下核心要点:

  1. 二叉树的核心定义:它是一个递归定义的、每个节点最多有两个有序子节点(左、右)的树结构。这个“有序”是其区别于普通树的关键。
  2. 三大基本性质:我们掌握了关于节点数、深度、叶子节点之间关系的三个基本性质,它们是进行算法分析的理论基础。
  3. 两种特殊形态:我们辨析了满二叉树(完美形态)和完全二叉树(实用形态)。尤其是完全二叉树,它的节点编号与父子关系间的数学规律,是理解数组存储的关键。
  4. 两种存储结构:我们探讨了链式存储顺序(数组)存储。链式存储灵活通用,适用于任何二叉树;顺序存储则在处理完全二叉树时,凭借其高效的寻址能力和空间效率大放异彩。

二叉树的大门已经向您敞开。然而,仅仅定义和存储它还不够,如何有效地访问和操作树中的每一个节点,即“遍历”,才是发挥其威力的下一步。在下一篇文章**【数据结构与算法-Day 21】中,我们将深入探索二叉树的深度优先搜索(DFS)**,学习经典的前序、中序、后序三种遍历方式。敬请期待!


http://www.dtcms.com/a/357734.html

相关文章:

  • 最新SF授权系统源码全开源无加密v5.2版本
  • 什么是Jmeter? Jmeter工作原理是什么?
  • 平安健康平安芯医AI解析:7×24小时问诊+95%诊断准确率,人文温度短板与医生效能提升引热议
  • 【完整源码+数据集+部署教程】高速公路施工区域物体检测系统源码和数据集:改进yolo11-RepNCSPELAN
  • 手写链路追踪
  • 基于Net海洋生态环境保护系统的设计与实现(代码+数据库+LW)
  • 【面试场景题】怎么做业务领域划分
  • 互联网大厂AI大模型面试解析:从基础技术到场景应用
  • Jetson进行旋转目标检测推理实现大疆无人机飞行控制
  • Python-GEE遥感云大数据分析、可视化与Satellite Embedding应用
  • leetcode算法刷题的第二十一天
  • 阿里云服务器购买流程:四种主要购买方式图文教程详解与选择参考
  • Cherrystudio的搭建和使用
  • Silvaco TCAD | Victory DoE的基本使用方法(三)
  • 小杰机器视觉(six)——模板匹配
  • LeetCode 01背包 494. 目标和
  • 顶点 (VS)vs 片段(FS):OpenGL纹理滚动着色器的性能博弈与设计哲学
  • Java进阶教程之多线程与并发编程
  • Windows下快速配置UDF编译环境的详细步骤
  • VexCL并行异构库介绍和使用
  • Python Imaging Library (PIL) 全面指南:PIL图像处理异常处理与优化
  • oceanbase-参数及变量的记录
  • LeetCode 刷题【56. 合并区间】
  • 新人桌球笔记
  • Apisix工作流程
  • 主流国产数据库:文档完备性
  • 进程与线程的根本区别
  • 【双指针 - LeetCode】42. 接雨水
  • gstreamer使用hook的简单示例
  • 用户自定义字段(Custom Fields)设计方案,兼顾多语言、分组、校验、权限、查询性能、审计与多租户