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

Python数据结构与算法(6.1)——树

Python数据结构与算法(4.1)——树

    • 0. 学习目标
    • 1. 树的基本概念
      • 1.1 常用术语
      • 1.2 树的抽象数据类型
      • 1.3 树的应用场景
    • 2. 树的实现
      • 2.1 链式存储实现
      • 2.2 顺序存储实现
      • 2.3 实现对比
    • 3. 树的应用
      • 3.1 链树
      • 3.2 顺序树
    • 小结

0. 学习目标

树 (Tree) 是程序设计和算法中最重要的非线性数据结构之一,它以层次化的方式组织数据,广泛应用于文件系统、数据库索引、表达式解析、网络路由等场景。本节将首先介绍树的基本概念及其抽象数据类型 (ADT),然后讲解常见的两种存储实现方式,并通过示例验证其操作;最后结合实际问题,演示如何利用树实现复杂算法。
通过本节学习,应掌握以下内容:

  • 树的基本概念、术语及常见类型
  • 树的抽象数据类型定义及核心操作
  • 链式存储与顺序存储(数组表示)的实现方法及时间空间复杂度分析
  • 常用遍历算法(前序、中序、后序、层次)及其实现
  • 利用树解决实际问题(表达式求值等)

1. 树的基本概念

在计算机科学中,树 (Tree) 是一种表示层次关系的非线性抽象数据结构,由节点 (Node) 和连接节点的边 (Edge) 组成。每个节点最多只有一个父节点,但可以连接任意数量的子节点,这意味着树中不存在环路或循环结构。根节点 (Root) 是没有父节点的节点,作为树的起始点;而没有子节点的节点称为叶节点 (Leaf)。树中每个节点可以拥有零个或多个子节点,适用于文件系统、组织结构等场景。

1.1 常用术语

树 (Tree):由节点 (Node) 和连接节点的边 (Edge) 组成的层次结构。
根节点 (Root):没有父节点的唯一节点。
子节点 (Child) 与父节点 (Parent):父节点是直接与子节点相连且位于上层的节点,子节点是其下层节点。
叶子节点 (Leaf):没有子节点的节点。
内部节点(Internal Node):至少有一个子节点的非根节点。
深度 (Depth):从根节点到某节点的边数;根的深度为 0
高度 (Height):从某节点到最深叶子的最长边数;整棵树的高度是根的高度。
度 (Degree):节点拥有的子节点个数;树的度是所有节点度的最大值。
二叉树 (Binary Tree):度不超过 2 的有序树,子节点分别称为左子树和右子树。

树

1.2 树的抽象数据类型

ADT Tree
 数据对象: D = n i ∣ n i ∈ Node , i = 1 , 2 , … D={n_i\mid n_i\in\text{Node},i=1,2,\dots} D=niniNode,i=1,2,
 数据关系: R = ⟨ n i , n j ⟩ ∣ a i , a i + 1 ∈ D , i = 1 , 2 , . . . , n − 1 R={\langle n_i,n_j\rangle|a_i,a_{i+1}∈D,i=1,2,...,n-1} R=ni,njai,ai+1D,i=1,2,...,n1
 基本操作:
  1. __init__():初始化空树
  2. is_empty():判断树是否为空
  3. add_root(data):创建根节点
  4. add_child(parent, data):在指定父节点下添加子节点
  5. remove(node):删除指定节点及其子树
  6. traverse_preorder(mode):按指定模式遍历树
  7. find(data):查找值为 data 的节点

1.3 树的应用场景

树具有广泛的应用场景,例如:

  • 文件系统:目录与文件的层次管理
  • XML/JSON 解析:数据以树状结构存储
  • 编译原理:抽象语法树用于表达式和程序结构
  • 数据库索引:如 B 树、B+ 树、红黑树等
  • 路由表和决策树:网络分组转发与机器学习

2. 树的实现

树的存储方式主要有两种:链式存储(每个节点保存对其子节点的引用)和顺序存储(使用列表保存所有节点及其父节点索引)。

2.1 链式存储实现

树的链式存储中,每个节点包含数据和指向其子节点的指针。使用一个列表 (children) 来存储子节点。每个节点可能有多个子节点,因此我们使用一个列表来保存这些子节点的引用:

class ChainNode:def __init__(self, data):self.data = dataself.children = []  # 存放子节点引用def __str__(self):return str(self.data)
2.1.1 树的初始化

初始化空树,即将根节点设置为 None

class Tree:def __init__(self):self.root = None
2.1.2 树判空

判断树是否为空,当根节点为 None 时,树为空:

    def is_empty(self):return self.root is None
2.1.3 创建根节点

创建根节点,若已有根则抛出异常;返回新建的根节点引用:

    def add_root(self, data):if self.root is not None:raise Exception("根节点已存在")self.root = ChainNode(data)return self.root
2.1.4 创建子节点

在指定父节点下添加子节点,父节点不存在时抛出异常;返回新建的子节点引用:

    def add_child(self, parent, data):if parent is None:raise Exception("父节点不存在")node = ChainNode(data)parent.children.append(node)return node
2.1.5 删除指定节点

删除指定节点及其整个子树,若删除根则将树置为空:

    def remove(self, node):if self.root is None or node is None:returnif node is self.root:self.root = Nonereturndef _remove_from_parent(cur, target):for child in cur.children:if child is target:cur.children.remove(child)return Trueif _remove_from_parent(child, target):return Truereturn False_remove_from_parent(self.root, node)
2.1.6 遍历树

按指定模式遍历树,支持前序 (preorder)、后序 (postorder) 和层序 (levelorder):
前序遍历:也称深度优先遍历,首先访问根节点,然后递归依次遍历各子树
后序遍历:首先对节点的各子树依次递归遍历,然后访问根节点
层序遍历:也成广度优先遍历,按层次从上至下、从左至右依次访问节点

    def traverse(self, mode='preorder'):if self.root is None:return []result = []if mode == 'preorder':def dfs_pre(node):result.append(node.data)for ch in node.children:dfs_pre(ch)dfs_pre(self.root)elif mode == 'postorder':def dfs_post(node):for ch in node.children:dfs_post(ch)result.append(node.data)dfs_post(self.root)elif mode == 'levelorder':from collections import dequeq = deque([self.root])while q:cur = q.popleft()result.append(cur.data)for ch in cur.children:q.append(ch)else:raise ValueError("不支持的遍历模式:" + mode)return result
2.1.7 查找

查找值为 data 的节点,返回第一个匹配的节点引用,否则返回 None

    def find(self, data):def dfs(node):if node.data == data:return nodefor ch in node.children:found = dfs(ch)if found:return foundreturn Noneif self.root is None:return Nonereturn dfs(self.root)

2.2 顺序存储实现

在树的顺序存储中,我们通常使用数组(或列表)来存储树的节点信息,并通过节点的父子关系来定位每个节点的位置,每个节点的子节点在数组中的位置是连续的。

2.2.1 树的初始化

初始化空树,数据和值列表均为空:

class SeqTree:def __init__(self):self.data_list = []self.parent_list = []
2.2.2 判树空

判断树是否为空,当 data_list 为空时返回 True

    def is_empty(self):return len(self.data_list) == 0
2.2.3 创建根节点

创建根节点,将 data_list 添加根值,parent_list 添加 -1;返回根节点索引:

    def add_root(self, data):if not self.is_empty():raise Exception("根已存在")self.data_list.append(data)self.parent_list.append(-1)return 0
2.2.4 创建子节点

在指定父节点索引下添加子节点,索引无效时抛出异常;返回新节点索引:

    def add_child(self, parent_idx, data):if parent_idx < 0 or parent_idx >= len(self.data_list):raise IndexError("父节点索引无效")idx = len(self.data_list)self.data_list.append(data)self.parent_list.append(parent_idx)return idx
2.2.5 删除指定节点

删除指定节点及其子树,先收集所有待删除节点索引,再降序删除并修正父索引:

    def remove(self, idx):if idx < 0 or idx >= len(self.data_list):returnto_del = set()def dfs_del(i):to_del.add(i)for j, p in enumerate(self.parent_list):if p == i:dfs_del(j)dfs_del(idx)for i in sorted(to_del, reverse=True):self.data_list.pop(i)self.parent_list.pop(i)self.parent_list = [p-1 if p > i else p for p in self.parent_list]
2.6 遍历树

按指定模式遍历树,支持 preorderpostorderlevelorder

    def traverse(self, mode='preorder'):if self.is_empty():return []result = []if mode == 'preorder':def dfs(i):result.append(self.data_list[i])for j, p in enumerate(self.parent_list):if p == i:dfs(j)dfs(0)elif mode == 'postorder':def dfs(i):for j, p in enumerate(self.parent_list):if p == i:dfs(j)result.append(self.data_list[i])dfs(0)elif mode == 'levelorder':from collections import dequeq = deque([0])while q:i = q.popleft()result.append(self.data_list[i])for j, p in enumerate(self.parent_list):if p == i:q.append(j)else:raise ValueError("不支持的遍历模式:" + mode)return result
2.7 查找

查找值为 data 的节点,返回第一个匹配的索引,否则返回 None

   def find(self, data):for i, d in enumerate(self.data_list):if d == data:return ireturn None

2.3 实现对比

特性链式存储顺序存储
存储结构节点对象 + 动态列表Python 列表
插入 O ( 1 ) O(1) O(1) O ( 1 ) O(1) O(1),但可能触发扩容复制
删除 O ( n ) O(n) O(n) O ( n ) O(n) O(n)
遍历递归/迭代索引计算
空间利用率与节点个数成正比需预留/扩容,存在浪费

3. 树的应用

3.1 链树

首先初始化一个链树,然后测试相关操作:

tree = ChainTree()
r = tree.add_root('A')
b = tree.add_child(r, 'B')
c = tree.add_child(r, 'C')
tree.add_child(b, 'D')
tree.add_child(b, 'E')
print(tree.traverse('preorder'))
print(tree.find('E').data)
tree.remove(b)
print(tree.traverse('levelorder'))

输出结果如下所示:

['A', 'B', 'D', 'E', 'C']
E
['A', 'C']

3.2 顺序树

初始化一个顺序树,然后测试相关操作:

st = SeqTree()
r_idx = st.add_root('A')
b_idx = st.add_child(r_idx, 'B')
c_idx = st.add_child(r_idx, 'C')
st.add_child(b_idx, 'D')
st.add_child(b_idx, 'E')
print(st.traverse('postorder'))
print(st.find('C'))
st.remove(b_idx)
print(st.traverse('levelorder'))

输出结果如下所示:

['D', 'E', 'B', 'C', 'A']
2
['A', 'C']

小结

本节系统地介绍了树的基本术语和结构,包括节点、边、根节点、父子关系、度、深度和高度等概念,建立对树结构的清晰认知。随后,通过详细的代码,展示了树的构建及其常用的遍历方法,包括前序、后序和层次遍历。

相关文章:

  • 鸿蒙网络编程系列53-仓颉版TCP连接超时分析示例
  • python中的文件操作处理:文本文件的处理、二进制文件的处理
  • Android音视频多媒体开源框架基础大全
  • 基于Docker实现frp之snowdreamtech/frps
  • window显示驱动开发—为 DirectX VA 2.0 扩展模式提供功能(一)
  • 【JVM】- 类加载与字节码结构1
  • Spring AI详细使用教程:从入门到精通
  • RabbitMQ缓存详解:由来、发展、核心场景与实战应用
  • ubuntu之坑(十四)——安装FFmpeg进行本地视频推流(在海思平台上运行)
  • 软件工程的实践
  • ffmpeg subtitles 字幕不换行的问题解决方案
  • Yarn与NPM缓存存储目录迁移
  • MySQL查询缓存深度剖析
  • ffmpeg rtmp推流源码分析
  • 3GPP协议PDF下载
  • 【信创-k8s】重磅-鲲鹏arm+麒麟V10离线部署k8s1.30+kubesphere4.1.3
  • 从SQL Server到分布式大数据平台:重构企业数据架构
  • 四数之和-力扣
  • Python让自动驾驶“看见未来”:环境建模那些事儿
  • GaussDB 分布式数据库调优(架构到全链路优化)
  • 网站备案怎么备案/今日国际军事新闻最新消息
  • 茶山东莞网站建设/深圳市网络seo推广平台
  • iis建设网站教程/发稿服务
  • 柬埔寨网站建设/seo01网站
  • html5网站开发框架/南宁网站建设服务公司
  • wordpress+游戏网站/怎么样建网站