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

数据结构:树(Tree)

目录

我们为什么需要“树”?

树的专业术语 (Terminology)

二叉树 (Binary Tree) 的一步步推导

二叉树的严格定义

如何用代码表示一个二叉树节点?


我们为什么需要“树”?

在学习“树”之前,你最熟悉的数据结构应该是数组 (Array)链表 (Linked List)。我们先分析一下它们的优缺点,看看能不能找到一个“两全其美”的方案。

1. 数组 (Array):

优点: 访问速度极快。

因为内存是连续的,我们可以通过索引(下标)直接计算出元素的地址,所以查询任何一个元素的时间复杂度是 O(1)。

缺点: 插入和删除操作非常慢。

假设你有一个很大的、排好序的数组,要在中间插入一个元素,你需要把这个元素之后的所有元素都向后移动一位。删除同理。

这个过程的时间复杂度是 O(n),其中 n 是元素的数量。此外,数组的大小通常是固定的,扩容很麻烦。

2. 链表 (Linked List)

优点: 插入和删除操作非常快。

我们只需要改变目标位置前后节点的指针指向即可,时间复杂度是 O(1)(前提是已经找到了要操作的位置)。

缺点: 访问和搜索速度很慢。因为内存是分散的,你没办法直接跳到第 i 个元素,必须从头节点开始,一个一个地“遍历”过去,时间复杂度是 O(n)。

矛盾出现了:

我们想要一个数据结构,既能像数组一样快速搜索,又能像链表一样快速增删

推导开始:

  • 链表的慢,根源在于它是一个线性结构,只有一个“下一个”(next)的方向。要找一个元素,最坏情况要走遍整条链。

  • 怎么加速呢?如果我们从一个节点出发,能有好几个“下一个”选项,是不是就能更快地“缩小范围”?

  • 比如说,我们站在一个节点,左边指过去,都是比它小的数;右边指过去,都是比它大的数。这样一来,每次比较,我们都能排除掉大约一半的元素,搜索速度会大大提升。

这个“一个节点指向多个节点”的想法,就打破了“线性”的束缚。

这种一个节点(父)连接到一个或多个节点(子)的结构,看起来就像现实世界里倒挂的树。

 “树”结构,就是为了解决数组和链表在“查找效率”和“增删效率”上的根本矛盾而诞生的。它是一种非线性 (Non-linear) 的数据结构。


树的专业术语 (Terminology)

在深入学习之前,必须先掌握这些基本概念,这是我们交流的“语言”。

                A(根节点 Root)/       \B(Parent)   C(Parent)/   \           \D       E           F(Leaf)  (Leaf)      /   \G     H(Leaf) (Leaf)

对应解释(结合上图)

  1. 节点(Node)

    • 树的基本组成单位,如 A、B、C、D、E、F、G、H 都是节点。

  2. 根节点(Root)

    • 树最上层的节点,没有父节点。

    • 在上图中,A 是根节点。

  3. 边(Edge)

    • 连接两个节点的线,例如 (A,B)(B,E)(F,H) 等。

  4. 父节点(Parent)

    • 一个节点的直接上层节点。

    • B 的父节点是 A;E 的父节点是 B。

  5. 子节点(Child)

    • 父节点直接连接的下层节点。

    • B 的子节点是 D 和 E。

  6. 兄弟节点(Siblings)

    • 拥有相同父节点的节点。

    • D 和 E 是兄弟节点;G 和 H 也是兄弟节点。

  7. 叶子节点 / 终端节点(Leaf Node / Terminal Node)

    • 没有子节点(度为 0)的节点。

    • D、E、G、H 是叶子节点。

  8. 内部节点(Internal Node)

    • 非叶子节点,即至少有一个子节点。

    • A、B、C、F 是内部节点。

  9. 子树(Subtree)

    • 由任意一个节点及其所有后代节点组成的树。

    • 以 C 为根节点的子树是 {C, F, G, H}

  10. 节点的度(Degree of a Node)

    • 一个节点拥有的子节点数量。

    • B 的度是 2(D 和 E);F 的度是 2(G 和 H)。

  11. 树的度(Degree of a Tree)

    • 树中所有节点度的最大值。

    • 上图最大度是 2(B 和 F),所以树的度为 2。

  12. 层(Level)

    • 从根开始,根为第 0 层,依次向下。

    • A 在第 0 层,B 和 C 在第 1 层,D、E、F 在第 2 层,G、H 在第 3 层。

  13. 深度(Depth)

    • 从根节点到某个节点经过的边数。

    • E 的深度是 2(A→B→E)。

  14. 高度(Height)

    • 从某个节点到最远叶子节点的边数。

    • 树的高度是根节点 A 的高度,这里是 3(A→C→F→G/H)。

中文术语英文术语解释
节点Node树的基本组成部分。它包含数据和指向其他节点的指针。
根节点Root树顶端的节点,它没有父节点。一棵树只有一个根节点。
Edge连接两个节点的线。
父节点Parent一个节点所连接的上层节点。
子节点Child一个节点所连接的下层节点。
兄弟节点Siblings拥有相同父节点的节点们。
叶子节点 / 终端节点Leaf Node / Terminal Node没有任何子节点的节点(度为0的节点)。
内部节点Internal Node非叶子节点,即至少有一个子节点的节点。
子树Subtree树中任意一个节点和它下面的所有后代节点组成的结构。
节点的度Degree of a Node一个节点拥有的子树数量(或子节点数量)。
树的度Degree of a Tree树中所有节点度的最大值。
Level从根节点开始,根为第0层,根的子节点为第1层,以此类推。
深度Depth从根节点到某个节点所经过的边的数量。根节点的深度是0。
高度Height从某个节点到其最远叶子节点所经过的边的数量。树的高度是指根节点的高度。

二叉树 (Binary Tree) 的一步步推导

我们已经知道,树的一个节点可以有任意多个子节点。但在实际应用中,这“任意多”会带来不确定性,导致结构复杂,不方便实现。

第一性推导:如何简化“树”这个通用模型?

  • 最简单的“树”是什么?每个节点最多只有一个子节点。

  • 如果你画出来,会发现这其实就是一个链表。它又回到了线性的老路。

  • 那么,在“一个子节点”(链表)的基础上,稍微复杂化一点,但又保持结构足够简单,该怎么办?

  • 答案就是:让每个节点最多有两个子节点。

这个“最多有两个子节点”的树,就是二叉树 (Binary Tree)

它是在通用树的复杂性和链表的简单性之间取得的完美平衡,也是实际应用中最常见、最重要的树形结构。


二叉树的严格定义

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

  • 要么是空集(空树)。

  • 要么由一个根节点和两棵互不相交的、分别称为根节点的左子树 (Left Subtree)右子树 (Right Subtree) 的二叉树组成。

关键点:

  1. 最多两个:节点可以有0个、1个或2个子节点。

  2. 有序区分:左子树和右子树是严格区分的,不能颠倒。一个节点只有一个子节点时,也要明确它是左孩子还是右孩子。

如何用代码表示一个二叉树节点?

根据定义,一个节点需要:

  1. 一块空间来存储数据本身 (Data)。

  2. 一个指针指向它的左孩子。如果左孩子不存在,就指向NULL

  3. 一个指针指向它的右孩子。如果右孩子不存在,就指向NULL

所以,最自然的 C/C++ 结构就产生了:

C++ 版本:

struct TreeNode {int data;            // 节点存储的数据,这里用 int 举例TreeNode* left;      // 指向左子树的指针 (left child pointer)TreeNode* right;     // 指向右子树的指针 (right child pointer)
};

C 版本:

typedef struct TreeNode {int data;struct TreeNode* left;struct TreeNode* right;
} TreeNode;
  • data 用来存放节点的数值。

  • left 指针指向左边的孩子节点,没有就是 NULL

  • right 指针指向右边的孩子节点,没有就是 NULL

一棵完整的树,就是由很多这样的TreeNode通过leftright指针互相连接而成的。

我们通常会持有一个指向根节点的指针(TreeNode* root)来代表整棵树。

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

相关文章:

  • c++中的Lambda表达式详解
  • Linux 对 YUM 包的管理
  • 20250814荣品RD-RK3588开发板在Rockchip原厂的buildroot【linux-5.10】下让eth0网卡跑iperf2测试网速
  • 机器学习初学
  • Linux 编译过程中遇到 TMPDIR 空间不足的问题
  • FPGA读取AHT20温湿度模块思路及实现,包含遇到的问题(IIC协议)
  • 举例说明环境变量及 PATH 的作用
  • ODE-by-Matlab-01-人口增长模型
  • Java进阶学习之Stream流的基本概念以及使用技巧
  • 不用编程不用组态,实现各种PLC之间数据通讯的网络结构示意图
  • Cookie、Session、Token详解
  • week1-[分支嵌套]公因数
  • P1281 [CERC1998] 书的复制
  • 跨域及解决方案
  • Product Hunt 每日热榜 | 2025-08-14
  • httpx 设置速率控制 limit 时需要注意 timeout 包含 pool 中等待时间
  • Effective C++ 条款40:明智而审慎地使用多重继承
  • 20道Vue框架相关前端面试题及答案
  • Uniapp 中 uni.request 的二次封装
  • stm32f103rct6开发板引脚图
  • 芯伯乐1MHz高频低功耗运放芯片MCP6001/2/4系列,微安级功耗精密信号处理
  • UML函数原型中stereotype的含义,有啥用?
  • 打靶日常-CSRF
  • 中国车企全球化数字转型标杆案例:SAP系统多项目整合升级实践
  • 考研408《计算机组成原理》复习笔记,第五章(2)——CPU指令执行过程
  • Day 11: 预训练语言模型基础 - 理论精华到实战应用的完整指南
  • k8s+isulad 网络问题
  • 【奔跑吧!Linux 内核(第二版)】第7章:系统调用的概念
  • 基本电子元件:电阻器
  • 读书笔记:《我看见的世界》