【13】数据结构之树结构篇章

目录标题

  • 树Tree
    • 树的定义
    • 树的基本概念
    • 树的存储结构
      • 双亲表示法
      • 孩子表示法
      • 孩子兄弟表示法
    • 二叉树
      • 二叉树与度不超过2的普通树的不同之处
      • 二叉树的基本形态
      • 二叉树的分类
      • 二叉树的性质
    • 二叉树的顺序存储
    • 二叉树的链式存储
      • 二叉树的链式存储的结点结构
      • 树的遍历
        • 先序遍历
        • 中序遍历
        • 后序遍历
        • 输出二叉树的叶子结点
        • 输出二叉树叶子结点的个数
        • 输出二叉树的高度
        • 链表存储的调试与代码集合

树Tree

  • 数据结构中具有层次关系的非线性结构.
  • 生活中常见各种树结构的应用
    • 家谱树
      在这里插入图片描述
    • Windows文件系统
      在这里插入图片描述

树的定义

  • 树为n个结点的有限集合T.

    • n=0,没有结点,称为空树.
    • n>0,必有一根.
      • 根root结点,没有前驱结点.
      • 其余n-1个结点可以划分成m棵根的子树.
  • 特点

    • 如下图,A为根结点,B、C、D为根结点的子树.
    • 子树又有多棵子树组成.
    • 每棵子树除根节点外,其余每个结点有且仅有一个直接前驱,但可以有0个或多个直接后继.
    • 形成树的递归特性.
      在这里插入图片描述

树的基本概念

  • 结点:包含一个数据元素及若干指向其他结点的分支信息.
  • 结点分类:根结点、叶子结点、非叶子结点、孩子结点、双亲结点、兄弟结点以及堂兄弟结点.
  • 结点的度:一个结点的子树个数.
  • 树的度:树中所有结点的度的最大值.
  • 叶子结点:度为0的结点.
  • 分支结点:度不为0的结点.
  • 结点的层次:从根结点开始定义,根结点的层次为1,根的直接后继的层次为2,以此类推.
  • 结点的层序编号:将树中的结点按从上到下、同层按从左到右的次序排成一个线性序列,依次给他们编以连续的自然数.
  • 森林:m棵互不相交的树的集合.
  • 孩子结点:相邻的两层之间用直线连接的层数更下面的结点.
  • 双亲结点:相邻的两层之间用直线连接的层数更上面的结点.
  • 兄弟结点:同一层的结点之间为兄弟结点.
  • 堂兄弟结点:同一层的双亲不同的结点.
  • 祖先结点:当前结点中所有上面层级与之链接的一脉结点.
  • 子孙结点:当前结点中所有下面层级且与之链接的一脉结点.
  • 前辈结点:当前结点以上层级的所有结点.
  • 后辈结点:当前结点以下层级的所有结点.
  • 有序树:如果在树的每一组兄弟结点之间定义一个从左到右的次序,则得到一颗有序树.
    在这里插入图片描述

树的存储结构

双亲表示法

  • 定义:用一组连续的存储单元存储树的每个结点,每个结点设置指针域parent指向双亲.
  • 根结点指针域设置为-1.
  • 按层序将每个结点编号.
  • 按结点的层序编号,依次在列表中对应单元存储一个结点(data, parent).
    • data:存储树结点中的数据元素.
    • parent:存储该结点的双亲结点的列表下标值.
  • 存储代码定义
class Node(object):def __init__(self, data, parent):self.data = dataself.parent = parent
  • 树的示意图
    在这里插入图片描述
  • 双亲表示存储图
    在这里插入图片描述

孩子表示法

  • 定义:把每个结点的孩子结点排列起来,以单链表为存储结构.
  • 存储结构将所有结点按层序编号顺序存储在列表中,用单链表的存储结构表示该结点的孩子结点.
  • 树的示意图
    在这里插入图片描述
  • 存储结构表
    在这里插入图片描述

孩子兄弟表示法

  • 定义:树的左孩子右兄弟表示法指的是左边的孩子结点接管右边的孩子结点,链表中每个结点的两个链域分别指向该结点的左孩子和右兄弟.
  • 采用孩子兄弟表示法进行转换的流程示意图:
    在这里插入图片描述

二叉树

  • 作为一类非常重要的特殊的树状结构.
  • 特点每个结点至多有2个孩子结点,并且有左右之分.
  • 左边的孩子成为左孩子结点,位于右边的孩子结点称为右孩子结点.

二叉树与度不超过2的普通树的不同之处

  • 在普通树中,若结点只有一个孩子结点,无左右之分.
  • 二叉树为有序数,左子树和右子树的次序不能颠倒,即使树中某个结点只有一棵子树,也要区别是左子树还是右子树.
  • 在有序树中,虽然一个结点的孩子结点之间是有左右次序之分的,但是若该结点只有一个孩子结点,则无须区分其左右次序.
  • 在二叉树中,即使是一个孩子结点也要做出左右孩子结点.

二叉树的基本形态

  • 1.空二叉树
  • 2.仅有根结点的二叉树
  • 3.仅有一棵左子树的二叉树
  • 4.仅有一棵右子树的二叉树
  • 5.有两棵子树的二叉树

二叉树的分类

  • 满二叉树:一棵高度为k且有2^k-1个结点的二叉树

    • 叶子结点只能出现在最后一层,
    • 非叶子结点都在左右子树,
    • 在同样高度的二叉树中,满二叉树的结点数最多,叶子数最多.
      在这里插入图片描述
  • 完全二叉树:若满二叉树最后一层的结点,从右向左连续缺若干结点,就是完全二叉树

    • 叶子结点只能出现在最下两层,
    • 最下层的叶子结点一定集中在左边连续位置,
    • 如果结点的度为1,那么该结点只有左孩子结点,不存在只有右孩子结点的情况,
    • 有同样结点数的二叉树,完全二叉树的高度最小.
      在这里插入图片描述
  • 联系与区别

    • 若某个结点没有左孩子的结点,那么它一定没有右孩子结点
    • 满二叉树除叶子结点外,其中每个结点都有两个孩子结点,每层的结点数都达到最大
    • 满二叉树一定也是完全二叉树,但完全二叉树不一定为满二叉树.

二叉树的性质

  • 1.若二叉树的层次从1开始计数,则在二叉树的第i层最多有2^(i-1)个结点.
  • 2.高度为k的二叉树最多有(2^k)-1个结点.
  • 3.高度为k的二叉树最少有k个结点.
  • 4.具有n个结点的二叉树的高度最多为n.
  • 5.具有n个结点的二叉树的高度最少为 log ⁡ 2 n + 1 \mathcal{}\log_2 n+1 log2n+1
  • 6.对于任何一棵二叉树,如果其叶子结点有a个,度为2的结点有b个,则有a=b+1.
  • 7.n个结点可以组成 1 n + 1 ⋅ ( 2 n ) ! n ! ⋅ n ! \frac{1}{n+1} \cdot \frac{(2n)!}{n! \cdot n!} n+11n!n!(2n)!种不同构的二叉树.
  • 8.具有n个结点的完全二叉树的高度为 log ⁡ 2 n + 1 \mathcal{}\log_2 n+1 log2n+1
  • 9.如果完全二叉树各层次结点从1开始编号,即1,2,3,…,n,那么则可得以下关系:
    • 仅当i=1时,结点i为根结点;
    • 当i>1时,结点i的双亲结点编号为i/2(取整);
    • 结点i的左孩子结点编号为2i;
    • 结点i的右孩子结点编号为2i+1;
    • 若2i>n,则结点i无左孩子结点;
    • 若2i+1>n,则结点i无右孩子结点.
      在这里插入图片描述

二叉树的顺序存储

  • 顺序存储实现

    • 层序编号 = 列表下标值+1
      在这里插入图片描述
  • 顺序存储的初始化

    def __init__(self, array):self.array = array  # 顺序存储的数组(完全二叉树形式)
  • 前序遍历
    def preOrder(self):"""前序遍历:根 -> 左 -> 右"""result = []def _preorder(index):if index >= len(self.array) or self.array[index] is None:returnresult.append(self.array[index])_preorder(2 * index + 1)  # 左子节点_preorder(2 * index + 2)   # 右子节点_preorder(0)  # 从根节点开始return result
  • 中序遍历
    def midOrder(self):"""中序遍历:左 -> 根 -> 右"""result = []def _inorder(index):if index >= len(self.array) or self.array[index] is None:return_inorder(2 * index + 1)result.append(self.array[index])_inorder(2 * index + 2)_inorder(0)return result
  • 后序遍历
    def postOrder(self):"""后序遍历:左 -> 右 -> 根"""result = []def _postorder(index):if index >= len(self.array) or self.array[index] is None:return_postorder(2 * index + 1)_postorder(2 * index + 2)result.append(self.array[index])_postorder(0)return result
  • 顺序存储的调试与代码集合
class SeqTree:def __init__(self, array):self.array = array  # 顺序存储的数组(完全二叉树形式)def preOrder(self):"""前序遍历:根 -> 左 -> 右"""result = []def _preorder(index):if index >= len(self.array) or self.array[index] is None:returnresult.append(self.array[index])_preorder(2 * index + 1)  # 左子节点_preorder(2 * index + 2)   # 右子节点_preorder(0)  # 从根节点开始return resultdef midOrder(self):"""中序遍历:左 -> 根 -> 右"""result = []def _inorder(index):if index >= len(self.array) or self.array[index] is None:return_inorder(2 * index + 1)result.append(self.array[index])_inorder(2 * index + 2)_inorder(0)return resultdef postOrder(self):"""后序遍历:左 -> 右 -> 根"""result = []def _postorder(index):if index >= len(self.array) or self.array[index] is None:return_postorder(2 * index + 1)_postorder(2 * index + 2)result.append(self.array[index])_postorder(0)return result# 测试案例
if __name__ == "__main__":print('PyCharm')# 测试树结构:#       1#     /   \#    2     3#     \   / \#      4 5   6arr = [1, 2, 3, None, 4, 5, 6]tree = SeqTree(arr)print("前序遍历:", tree.preOrder())      # 输出: [1, 2, 4, 3, 5, 6]print("中序遍历:", tree.midOrder())       # 输出: [2, 4, 1, 5, 3, 6]print("后序遍历:", tree.postOrder())     # 输出: [4, 2, 5, 6, 3, 1]
  • 顺序存储的缺点:
  • 由于必须按完全二叉树的形式来存储树中的结点,因此将造成存储空间的浪费,
  • 在最坏的情况下,一个只有k个结点的仅有右孩子结点的二叉树缺需要2^(k-1)个结点的存储空间.
  • 因此,满二叉树和完全二叉树适合使用顺序存储结构实现.

二叉树的链式存储

二叉树的链式存储的结点结构

  • 两个指针域left和right,分别指向左孩子和右孩子结点的指针
  • 一个数据域data用于存储该结点的数据元素
  • 二叉树链式存储结点
class Node(object):def __init__(self, data):self.data = dataself.left = Noneself.right = None

在这里插入图片描述

  • 三叉树链式存储结点
class Node(object):def __init__(self, data):self.data = dataself.parent = Noneself.left = Noneself.right = None

在这里插入图片描述

树的遍历

- 树的遍历按某种次序访问树中的结点,要求树中每个结点被访问一次且仅被访问一次.
先序遍历
  • 最先访问根结点,然后访问树的左子树,最后访问树的右子树.
    def preOrder(self, node):"""先序遍历:param node:树结点"""if node != None:print(node.data, end=",")self.preOrder(node.left)self.preOrder(node.right)
中序遍历
  • 最先访问树的左子树,然后访问根结点,最后是访问树的右子树.
  • 案例
    在这里插入图片描述
  • 遍历过程:
    • 1.A有左子树,先访问左子树.
    • 2.B没有左子树,输出B.
    • 3.D有左子树,访问其左子树.
    • 4.F没有左子树,输出F.
    • 5.F也没有右子树,返回F的根结点D,输出D.
    • 6.输出D之后,A的整个左子树遍历完毕,返回根结点A,输出A.
    • 7.C有左子树,先访问左子树.
    • 8.E无左子树,输出E.
    • 9.E无左右子树,返回根结点C,输出C.
    • C无右子树,则A的右子树遍历完毕.
    def midOrder(self, node):"""中序遍历:param node::return:"""if node != None:self.midOrder(node.left)print(node.data, end=",")self.midOrder(node.right)
后序遍历
  • 最先访问树的左子树,然后访问树的右子树,最后访问根结点.
    def postOrder(self, node):"""后序遍历:param node::return:"""if node != None:self.postOrder(node.left)self.postOrder(node.right)print(node.data, end=",")
输出二叉树的叶子结点
    def getLeaf(self, node):"""输出叶子结点:param node::return:"""if node != None:if node.left == None and node.right == None:print(node.data, end=" ")self.getLeaf(node.left)self.getLeaf(node.right)
输出二叉树叶子结点的个数
  • 递归方法实现:
    • 如果树为空,叶子结点个数为0
    • 如果只有1个结点,则为1
    • 否则,叶子结点个数 = 左子树叶子结点个数+右子树叶子结点个数
    def getLeafCount(self, node):"""输出叶子结点的个数:param node::return:"""if node == None:leafCount = 0elif node.left == None and node.right == None:leafCount = 1else:leafCount = self.getLeafCount(node.left) + self.getLeafCount(node.right)return leafCount
输出二叉树的高度
  • 递归实现
    • 若树为空树,则高度为0
    • 若树非空,高度应为其左右子树高度中的最大值加1.
    def height(self, node):"""求二叉树的高度:param node::return:"""if node != None:leafHeight = self.height(node.left)rightHeight = self.height(node.right)if leafHeight > rightHeight:max = leafHeightelse:max = rightHeightreturn max+1else:return 0
链表存储的调试与代码集合
class Node(object):def __init__(self, data):self.data = dataself.left = Noneself.right = Noneclass BinaryTree:def __init__(self, root = None):self.root = rootdef preOrder(self, node):"""先序遍历:param node:树结点"""if node != None:print(node.data, end=" ")self.preOrder(node.left)self.preOrder(node.right)def midOrder(self, node):"""中序遍历:param node::return:"""if node != None:self.midOrder(node.left)print(node.data, end=" ")self.midOrder(node.right)def postOrder(self, node):"""后序遍历:param node::return:"""if node != None:self.postOrder(node.left)self.postOrder(node.right)print(node.data, end=" ")def getLeaf(self, node):"""输出叶子结点:param node::return:"""if node != None:if node.left == None and node.right == None:print(node.data, end=" ")self.getLeaf(node.left)self.getLeaf(node.right)def getLeafCount(self, node):"""输出叶子结点的个数:param node::return:"""if node == None:leafCount = 0elif node.left == None and node.right == None:leafCount = 1else:leafCount = self.getLeafCount(node.left) + self.getLeafCount(node.right)return leafCountdef height(self, node):"""求二叉树的高度:param node::return:"""if node != None:leafHeight = self.height(node.left)rightHeight = self.height(node.right)if leafHeight > rightHeight:max = leafHeightelse:max = rightHeightreturn max+1else:return 0if __name__ == "__main__":#  构建示例树#       1#     /   \#    2     3#     \   / \#      4 5   6root = Node('1')root.left = Node('2')root.right = Node('3')root.left.right = Node('4')root.right.left = Node('5')root.right.right = Node('6')tree = BinaryTree(root)print("前序遍历:")tree.preOrder(root)print("\n中序遍历:")tree.midOrder(root)print("\n后序遍历:")tree.postOrder(root)print("\n树的叶子结点:")tree.getLeaf(root)print("\n输出叶子结点的个数:")print(tree.getLeafCount(root))print("输出二叉树的高度:")print(tree.height(root))

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

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

相关文章

雷达生命探测仪,地震救援的生命探测先锋|鼎跃安全

在地震、山体滑坡、坍塌建筑等突发灾害中,会严重摧毁建筑物,造成倒塌和人员被困;在瓦砾堆、混凝土板层中,受困人员的生命安全常常面临严峻威胁。传统救援手段通常存在响应时间长、监测精度有限等不足。 救援现场往往环境复杂&…

512天,倔强生长:一位技术创作者的独白

亲爱的读者与同行者: 我是倔强的石头_,今天是我在CSDN成为创作者的第512天。当系统提示我写下这篇纪念日文章时,我恍惚间想起了2023年11月19日的那个夜晚——指尖敲下《开端——》的标题,忐忑又坚定地按下了“发布”键。那时的我…

数据结构*集合框架顺序表-ArrayList

集合框架 常见的集合框架 什么是顺序表 顺序表是一种线性表数据结构,它借助一组连续的存储单元来依次存储线性表中的数据元素。一般情况下采用数组存储。 在数组上完成数据的增删查改。 自定义简易版的顺序表 代码展示: public interface IArray…

使用openpyxl时的一些注意点

一、是否需要close()? 在使用 openpyxl 时,wb.save() 后一般不需要再手动调用 wb.close()。wb.save() 会自动处理文件写入和释放。 如果是使用openpyxl.load_workbook(filename, read_onlyTrue) 打开了一个只读模式的工作簿,此时会建立文件…

Python爬虫第11节-解析库Beautiful Soup的使用上篇

目录 前言 一、Beautiful Soup 简介 1.1 Beautiful Soup概述 1.2 准备工作 1.3 解析器 二、基本使用 三、节点选择器的使用 3.1 选择元素 3.2 提取信息 3.2.1 获取名称 3.2.2 获取属性 3.2.3 获取内容 3.3 嵌套选择 3.4 关联选择 3.4.1 子节点和子孙节点 3.4.2…

【Docker-13】Docker Container容器

Docker Container(容器) 一、什么是容器? 通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有…

Spring Cache(笔记)

简介: 常用注解:

大模型Qwen32b(FP16精度)部署所需的显存大小和并发数计算分析

大家好,我是微学AI,今天给大家介绍一下大模型Qwen32b(FP16精度)部署所需的显存大小和并发计算分析。 文章目录 1. 大模型显存需求分析1.1 模型参数与显存占用1.2 不同精度对显存的影响 2. 不同显卡配置下的并发能力2.1 80G显卡并发能力2.2 64G显卡并发能…

【euclid】10.2 2D变换模块(transform2d.rs)Arbitrary trait

源码 #[cfg(feature "arbitrary")] impl<a, T, Src, Dst> arbitrary::Arbitrary<a> for Transform2D<T, Src, Dst> whereT: arbitrary::Arbitrary<a>, {fn arbitrary(u: &mut arbitrary::Unstructured<a>) -> arbitrary::Res…

MAC Mini M4 上测试Detectron2 图像识别库

断断续续地做图像识别的应用&#xff0c;使用过各种图像识别算法&#xff0c;一开始使用openCV 做教室学生计数的程序。以后又使用YOLO 做医学伤口检测程序。最近&#xff0c;开始使用meta 公司的Detectron2.打算做OCR 文档结构分析 Detectron2 的开发者是 Meta 的 Facebook AI…

一天时间,我用AI(deepseek)做了一个配色网站

前言 最近在开发颜色搭配主题的相关H5和小程序&#xff0c;想到需要补充一个web网站&#xff0c;因此有了这篇文章。 一、确定需求 向AI要答案之前&#xff0c;一定要清楚自己想要做什么。如果你没有100%了解自己的需求&#xff0c;可以先让AI帮你理清逻辑和思路&#xff0c;…

机器视觉用消色差双合透镜

光学系统案例&#xff1a;机器视觉用消色差双合透镜 一、设计规格 1. 应用场景&#xff1a;专为工业相机成像而设计&#xff0c;工作于可见光波段&#xff0c;旨在满足该领域对高精度成像的需求。 2. 核心参数&#xff1a; • 焦距&#xff1a;精确要求达到 50 mm 1%&#…

批量归一化(Batch Normalization)原理与PyTorch实现

批量归一化&#xff08;Batch Normalization&#xff09;是加速深度神经网络训练的常用技术。本文通过Fashion-MNIST数据集&#xff0c;演示如何从零实现批量归一化&#xff0c;并对比PyTorch内置API的简洁实现方式。 1. 从零实现批量归一化 1.1 批量归一化函数实现 import t…

feedback

这个文件 lib/pages/feedback/index.dart 是一个反馈/留言表单页面的实现&#xff0c;主要功能是&#xff1a; 表单收集功能&#xff1a; 真实姓名&#xff08;必填&#xff09;联系电话&#xff08;必填&#xff0c;需要验证手机号格式&#xff09;电子邮箱&#xff08;选填&a…

数据仓库标准库模型架构相关概念浅讲

数据仓库与模型体系及相关概念 数据仓库与数据库的区别可参考&#xff1a;数据库与数据仓库的区别及关系_数据仓库和数据库-CSDN博客 总之&#xff0c;数据库是为捕获数据而设计&#xff0c;数据仓库是为分析数据而设计 数据仓库集成工具 在一些大厂中&#xff0c;其会有自…

适用于 HAL 的 AIDL

目录 设计初衷 注意 编写AIDLHAL接口 查找AIDLHAL接口 扩展接口 将现有HAL从HIDL转换为AIDL AIDL与HIDL之间的主要差异 针对HAL的供应商测试套件(VTS)测试 Android 11 中引入了在 Android 中使用 AIDL 实现 HAL 的功能, 从而可以在不使用 HIDL 的情况下实现 Android 的部分…

leetcode0547. 省份数量-medium

1 题目&#xff1a;省份数量 官方标定难度&#xff1a;中 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相…

【专题刷题】双指针(一)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;1&#xff0c;本人解法 本人屎山代码&#xff1b;2&#xff0c;优质解法 优质代码&#xff1b;3&#xff0c;精益求精&#xff0c;…

WebSocket 技术详解

引言 在现代Web应用中&#xff0c;实时通信已经成为不可或缺的一部分。想象一下聊天应用、在线游戏、股票交易平台或协作工具&#xff0c;这些应用都需要服务器能够即时将更新推送给客户端&#xff0c;而不仅仅是等待客户端请求。WebSocket技术应运而生&#xff0c;它提供了一…

【redis】初识redis

初识redis Redis 是一种基于键值对&#xff08;key-value&#xff09; 的 NoSQL 的数据库&#xff0c;它与很多键值数据库不同&#xff0c; Redis 中的值可以是 string&#xff08;字符串&#xff09; 、hash&#xff08;哈希&#xff09;、list&#xff08;链表&#xff09;、…