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

【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 = data
	self.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:
                return
            result.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:
                return
            result.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


# 测试案例
if __name__ == "__main__":
    print('PyCharm')
    # 测试树结构:
    #       1
    #     /   \
    #    2     3
    #     \   / \
    #      4 5   6
    arr = [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 = data
		self.left = None
		self.right = None

在这里插入图片描述

  • 三叉树链式存储结点
class Node(object):
	def __init__(self, data):
		self.data = data
		self.parent = None
		self.left = None
		self.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 = 0
        elif node.left == None and node.right == None:
            leafCount = 1
        else:
            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 = leafHeight
            else:
                max = rightHeight
            return max+1
        else:
            return 0
链表存储的调试与代码集合
class Node(object):
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None


class BinaryTree:
    def __init__(self, root = None):
        self.root = root

    def 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 = 0
        elif node.left == None and node.right == None:
            leafCount = 1
        else:
            leafCount = self.getLeafCount(node.left) + self.getLeafCount(node.right)
        return leafCount

    def height(self, node):
        """
        求二叉树的高度
        :param node:
        :return:
        """
        if node != None:
            leafHeight = self.height(node.left)
            rightHeight = self.height(node.right)
            if leafHeight > rightHeight:
                max = leafHeight
            else:
                max = rightHeight
            return max+1
        else:
            return 0
            
if __name__ == "__main__":
	#  构建示例树
    #       1
    #     /   \
    #    2     3
    #     \   / \
    #      4 5   6
    root = 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))

相关文章:

  • AI自动化制作web和手机应用软件的利器:bolt.new
  • Python设计模式:策略模式
  • 主机协议端口安全
  • 网络问题之TCP/UDP协议
  • 【Windows自带的图片查看软件photo】
  • 回文日期2
  • Process Explorer 性能调优实战:精准定位资源泄漏与高负载进程
  • ZYNQ笔记(五):AXI GPIO 中断
  • C++指针(四)万字图文详解!
  • 【特权FPGA】之乘法器
  • P8623 [蓝桥杯 2015 省 B] 移动距离
  • go学习记录(第一天)
  • 一、TorchRec里边的输入输出类型
  • 为什么要将函数变量化?
  • django rest framework相关面试题
  • windows+cmake+vscode+NDK远程调试安卓端C++项目
  • 回文日期1
  • 泛微ECOLOGY9 记 数据展现集成 自定义开窗测试中对SQL 的IN语法转换存在BUG
  • Linux中OS的管理和进程的概念
  • 【力扣hot100题】(087)乘积最大子数组
  • 找国外公司做网站/google play谷歌商店
  • 淘宝美工/seo广告优化
  • 做网站文字要求/痘痘该如何去除效果好
  • 有梦商城公司网站/怎么做推广网站
  • 网站开发要学什么语言/百度的网址
  • 泉州软件开发公司/图片seo优化是什么意思