返回顶部
首页 > 资讯 > 后端开发 > Python >用Python实现数据结构之树
  • 444
分享到

用Python实现数据结构之树

数据结构之树Python 2023-01-30 23:01:39 444人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树

树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或称为树根。

  • 根节点:树中最顶部的元素

  • 父节点:处了根节点都有父节点,每个节点最多只有一个父节点

  • 孩子节点:一个父节点具有0个或多个孩子节点

  • 兄弟节点:同一个父节点的孩子节点之间是兄弟关系

  • 外部节点:一个没有孩子的节点称为外部节点,也叫叶子结点

  • 内部节点:有一个或多个孩子节点的节点叫做内部节点

  • 树的边:指一对节点(u,v),其中u是v的父节点或者v是u的父节点

  • 树的路径:一系列连续的边组成了一条路径

  • 节点的深度:节点的深度就是该节点的祖先的个数,不包括该节点本身,如果根节点的层数为1,则深度即为该节点的层数-1

  • 节点的高度:如果p是树中的叶子节点,那么它的高度为0.否则p的高度是它的孩子节点中的最大高度+1

  • 有序树:每个孩子之间有一定的顺序,例如:


class Tree():
    """
    树的抽象基类
    """

    # 叫做位置的内嵌类,用于封装节点
    class Position():

        def element(self):
            raise NotImplementedError('must be implemented by subclass')

        def __eq__(self, other):
            raise NotImplementedError('must be implemented by subclass')

    def root(self):
        """
        return 根节点的position
        """
        raise NotImplementedError('must be implemented by subclass')

    def parent(self,p):
        """

        :param p:一个位置对象
        :return: 返回p的父节点的position对象,如果p是根节点则饭后空
        """
        raise NotImplementedError('must be implemented by subclass')

    def num_children(self,p):
        """

        :param p:一个位置对象
        :return: 返回该位置的孩子节点的数量
        """
        raise NotImplementedError('must be implemented by subclass')

    def children(self,p):
        """

        :param p: 一个位置对象
        :return: 返回位置p的孩子的迭代
        """
        raise NotImplementedError('must be implemented by subclass')

    def __len__(self):
        """

        :return: 返回整个树的节点个数
        """

     raise NotImplementedError('must be implemented by subclass')

    def is_root(self,p):
        return self.root() == p

    def is_leaf(self,p):
        return self.num_children(p) == 0

    def is_empty(self):
        return len(self) == 0

这个抽象类中的方法必须在子类中实现才能调用,不然会产生NotImplementedError(‘must be implemented by subclass’)的异常

除此之外,对于Position()这个内嵌类可能较难理解,为什么要有这么一个内嵌类

这个内嵌类目前也是抽象类,具体方法都没有实现,但使用它的目的已经有了,就是将树中的节点进行封装,那为什么要封装节点呢?当调用树的相关方法时,节点可能为一个必要的参数,但我们手动传入时,实际上可以是任意的对象,这就会导致错误发生,所以我们必须保证传入的节点是节点的对象,同时也是本树对象的节点,不然就会弄混树与树的节点。同时将节点进行封装,可以避免使用者直接使用节点对象本身,相关节点的方法可以在封装成的Position对象调用。目前只是抽象类的定义,节点类等其他方法还未定义,后面还会看到具体的position对象的使用。

目前有了Tree这个抽象类,虽然其中的大多数方法还是抽象方法,但使用这些方法已经可以构成一些其他的功能了,所以就有了is_root,is_leaf,is_empty方法的定义。同时还可以定义计算节点的深度与高度的方法:


def depth(self,p):
    """
    计算节点在树中的深度
    """
    if self.is_root(p):
        return 0
    else:
        return 1 + self.depth(self.parent(p))

def height(self,p):
    """
    计算节点在树中的深度 
    """
    if self.is_leaf(p):
        return 0
    else:
        return 1 + max(self.height(c) for c in self.children(p))

我们现在介绍一种树的特殊化形式二叉树

二叉树的特点:

  • 每个父节点最多只有两个孩子节点

  • 两个孩子节点又叫做左孩子和右孩子

  • 以左孩子为根节点形成的树叫做左子树,以右孩子为根节点形成的树叫做右子树

  • 如果除了最下面的一层节点,其余节点组成的是一颗满二叉树,并且最下面的这层节点遵循从左到右依次添加的顺序,那么这个树就叫做完全二叉树

  • 非空完全二叉树中,外部节点数=内部节点数+1

二叉树的实现可以以继承树的抽象类的方式实现:


class BinaryTree(Tree):

    class node():

        def __init__(self, element, parent=None, left=None, right=None):
            self.element = element
            self.parent = parent
            self.left = left
            self.right = right

    class Position(Tree.Position):

        def __init__(self, container, node):
            self.container = container
            self.node = node

        def element(self):
            return self.node.element

        def __eq__(self, other):
            return isinstance(other, type(self)) and other.node is self.node

    def validate(self, p):
        """
        进行位置验证
        """
        if not isinstance(p, self.Position):
            raise TypeError('p must be proper Position type')
        if p.container is not self:
            raise ValueError('p does not belong to this container')
        if p.node.parent is p.node:
            raise ValueError('p is no longer valid')
        return p.node

    def make_position(self, node):
        """
        封装节点
        """
        return self.Position(self, node) if node is not None else None

    def __init__(self):
        self._root = None
        self.size = 0

    def __len__(self):
        return self.size

    def root(self):
        return self.make_position(self._root)

    def parent(self, p):
        node = self.validate(p)
        return self.make_position(node.parent)

    def left(self, p):
        node = self.validate(p)
        return self.make_position(node.left)

    def right(self, p):
        node = self.validate(p)
        return self.make_position(node.right)

    def sibling(self, p):
        parent = self.parent(p)
        if parent is None:
            return None
        else:
            if p == self.left(parent):
                return self.right(parent)
            else:
                return self.left(parent)

    def num_children(self, p):
        node = self.validate(p)
        count = 0
        if node.left is not None:
            count += 1
        if node.right is not None:
            count += 1
        return count
  

    def children(self,p):
        if self.left(p) is not None:
            yield self.left(p)
        if self.right(p) is not None:
            yield self.right(p)

代码中将之前的抽象方法进行了完整的定义,同时添加了validate与make_position方法。validate方法用于对传入的position参数进行验证,make_position方法用于将节点进行封装。除此之外还添加了二叉树特有的方法right,left和sibling,left与right分别返回节点的左孩子节点与右孩子节点,sibling返回的是节点的兄弟节点。

目前的二叉树的数据结构只是创建了一颗空树,我们接下来要加入的是对二叉树进行更新操作的方法


def add_root(self, e):
    if self._root is not None:
        raise ValueError('Root exists')
    self.size += 1
    self._root = self.Node(e)
    return self.make_position(self._root)

def add_left(self, e, p):
    node = self.validate(p)
    if node.left is not None:
        raise ValueError('Left child exists')
    self.size += 1
    node.left = self.Node(e, node)
    return self.make_position(node.left)

def add_right(self, e, p):
    node = self.validate(p)
    if node.right is not None:
        raise ValueError('Left child exists')
    self.size += 1
    node.right = self.Node(e, node)
    return self.make_position(node.right)

def replace(self, p, e):
    node = self.validate(p)
    old = node.element
    node.element = e
    return old

def delete(self, p):
    """
    删除该位置的节点,如果该节点有两个孩子,则会产生异常,如果只有一个孩子,
    则使其孩子代替该节点与其双亲节点连接
    """
    node = self.validate(p)
    if self.num_children(p) == 2:
        raise ValueError('p has two children')
    child = node.left if node.left else node.right
    if child is not None:
        child.parent = node.parent
    if node is self._root:
        self._root = child
    else:
        parent = node.parent
        if node is parent.left:
            parent.left = child
        else:
            parent.right = child
    self.size -= 1
    node.parent = node
    return node.element

总共加入了添加根节点,添加左孩子,添加右孩子,代替元素和删除节点5个方法,其中删除几点稍微有一些复杂,因为涉及到许多情况的判断。

到现在,一个完整的二叉树数据结构基本完成了。

但是我们还需要掌握一个算法,就是树的遍历算法

树的遍历一般有先序遍历,后序遍历,广度优先遍历(层序遍历),对于二叉树还有中序遍历

先序遍历

先序遍历是按照根节点->从左到右的孩子节点的顺序遍历,而且把每个孩子节点看作是子树的根节点同样如此,例如:

python实现先序遍历为:


def preorder(self,p):
    """
    先序遍历节点p为根节点的树
    """
    yield p
    for c in self.children(p):
        for other in self.preorder(c):
            yield other

虽然代码只有4行,但理解起来却不是很容易的,首先该方法是一个生成器,所以通过yield返回一个可迭代对象,也就是可以for循环该方法,由于是先序遍历,所以要先yield p,之后便要返回孩子节点,由于孩子节点可能还具有孩子,所以并不能只返回孩子节点,应该返回以孩子节点为根节点的树的所有节点,而要想for循环得到左右的孩子节点为根节点的所有节点,还需要调用孩子节点的先序遍历方法才能得到。总而言之,代码理解的难度还是由于递归算法造成的,一个复杂的递归终归还是不是那么容易就能看出来的。

后序遍历

后序遍历是按照先从左到右孩子节点->根节点,如图:

Python实现:


def postorder(self,p):
    """
    后序遍历节点p为根的树 
    """
    for c in self.children(p):
        for other in self.postorder(c):
            yield other
    yield p

理解与先序遍历相同

广度优先遍历

广度优先遍历也叫层序遍历,一层一层的遍历,如图:

用python实现:


def breadthfirst(self):
    if not self.is_empty():
        queue = Queue()
        queue.enqueue(self.root())
        while not queue.is_empty():
            p = queue.dequeue()
            yield p
            for i in self.children(p):
                queue.enqueue(i)

中序遍历二叉树

对于二叉树,遍历顺序为左孩子->父节点->右孩子

python实现为:


def inorder(self,p):
    if self.left(p) is not None:
        for other in self.inorder(self.left(p)):
            yield other
    if self.right(p) is not None:
        for other in self.inorder(self.right(p)):
            yield other

参考《数据结构与算法Python语言实现》

--结束END--

本文标题: 用Python实现数据结构之树

本文链接: https://lsjlt.com/news/181052.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 用Python实现数据结构之树
    树是由根结点和若干颗子树构成的。树是由一个集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构。在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树...
    99+
    2023-01-30
    数据结构 之树 Python
  • C++数据结构之AVL树的实现
    目录1.概念(1)二叉搜索树的缺点(2)定义节点2.插入(1)拆分(2)找节点与插节点(3)更新平衡因子与旋转3.判断4.完整代码及测试代码完整代码测试代码1.概念 (1)二叉搜索树...
    99+
    2024-04-02
  • C++数据结构之红黑树的实现
    目录一、什么是红黑树二、红黑树的约定三、红黑树vsAVL四、红黑树的实现1.找到插入的位置2.控制平衡3.测试代码五、完整代码1.test.c2.RBTree.h一、什么是红黑树 红...
    99+
    2022-11-13
    C++ 数据结构 红黑树 C++ 红黑树
  • C++数据结构之AVL树如何实现
    这篇文章主要讲解了“C++数据结构之AVL树如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++数据结构之AVL树如何实现”吧!1.概念(1)二叉搜索树的缺点要手撕AVL树,我们首先...
    99+
    2023-07-02
  • Python数据结构__树
    树是一种非常重要的数据结构,它是非线性结构,它不是Python内置的数据结构;树:  1.非线性结构,每个元素可以有多个前驱和后继;  2.树是n(n>=0)个元素的集合    n=0时,称为空树;    树只有一个特殊的没有前驱的元...
    99+
    2023-01-31
    数据结构 Python
  • 用Python实现数据结构之栈
    栈是最简单的数据结构,也是最重要的数据结构。它的原则就是后进先出(LIFO),栈被使用于非常多的地方,例如浏览器中的后退按钮,文本编辑器中的撤销机制,接下来我们用Python来具体实现这个数据结构。 栈中的方法 作为一个栈(用S来表示...
    99+
    2023-01-30
    数据结构 Python
  • Python 数据结构 tree 树
    树节点类 TreeNode 作为最简单的树节点,我们只需要3个基本属性 name: 当前节点的名字(使用str来保存) parent: 父节点对象(对根节点来说,该值为Null) child: 字节点对象们(使用dict来保存...
    99+
    2023-01-31
    数据结构 Python tree
  • 用Python实现数据结构之链表
    链表与栈,队列不一样,它是由一个个节点构成的,每个节点存储着本身的一些信息,也存储着其他一个或多个节点的引用,可以从一个节点找到其他的节点,节点与节点之间就像是有链连在一起一样,这种数据结构就叫做链表 单向链表是链表的最简单形式,链表...
    99+
    2023-01-30
    数据结构 链表 Python
  • 用Python实现数据结构之队列
    队列与栈的类型很相似,但它遵循的原则是先进先出(FIFO),也就是元素插入的时候只能在该数据结构的末端,而删除只能删除最前面的元素。队列同样应用广泛,例如打印机的队列或者是一个web服务器响应请求。 关于队列的方法 作为一个队列,同样...
    99+
    2023-01-30
    数据结构 队列 Python
  • Java数据结构之二叉查找树的实现
    目录定义节点结构查找算法插入算法删除算法完整代码定义 二叉查找树(亦称二叉搜索树、二叉排序树)是一棵二叉树,且各结点关键词互异,其中根序列按其关键词递增排列。 等价描述:二叉查找树中...
    99+
    2024-04-02
  • C++数据结构之搜索二叉树的实现
    目录零.前言1.概念2.作用3.迭代实现(1)查找(2)插入(3)删除4.递归实现(1)查找(2)插入(3)删除5.key/value模型的应用(1)对应查找(2)判断出现次数6.总...
    99+
    2024-04-02
  • Java数据结构之二叉排序树的实现
    目录1 二叉排序树的概述2 二叉排序树的构建2.1 类架构2.2 查找的方法2.3 插入的方法2.4 查找最大值和最小值2.5 删除的方法3 二叉排序树的总结1 二叉排序树的概述 本...
    99+
    2024-04-02
  • Python 数据结构之树的概念详解
    数据结构树简介 一、树简介 树(Tree)是一种抽象的数据结构,是一个数据的集合,集合中的数据组成了一个树状结构。例如上图,看起来像一棵倒挂的树,根朝上叶朝下。 树是由n(n>...
    99+
    2024-04-02
  • Python数据结构之树的全面解读
    目录前言🧡基本概念🌳树的定义🌲基本术语💚树的逻辑结构🍉前序遍历🍓后序遍历㇮...
    99+
    2024-04-02
  • Java数据结构学习之树
    目录一、树1.1 概念1.2 术语1.3 树的实现1.3.1 用数组来实现一棵树?1.3.2 用链表实现一棵树?1.3.3 树的转化1.4 二叉树1.4.1 二叉树的性质1.4.2 ...
    99+
    2024-04-02
  • Java数据结构之AVL树实例分析
    这篇文章主要介绍“Java数据结构之AVL树实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java数据结构之AVL树实例分析”文章能帮助大家解决问题。AVL树的引入搜索二叉树有着极高的搜索效...
    99+
    2023-06-30
  • Java数据结构之哈夫曼树概述及实现
    目录一、与哈夫曼树相关的概念二、什么是哈夫曼树三、哈夫曼树的构造方法四、哈夫曼树的代码实现一、与哈夫曼树相关的概念 概念 ...
    99+
    2024-04-02
  • Java数据结构之线索化二叉树的实现
    目录1.线索化二叉树的介绍2.线索化二叉树的基本特点3.线索化二叉树的应用案例4.前序线索化二叉树、遍历5.后序线索化二叉树1.线索化二叉树的介绍 将数列 {1, 3, 6, 8, ...
    99+
    2024-04-02
  • Java数据结构之线段树的原理与实现
    目录简介实现思路节点定义构建线段树求解区间和更新线段树简介 线段树是一种二叉搜索树,是用来维护区间信息的数据结构。可以在O(logN)的时间复杂度内实现单点修改、区间修改、区间查询(...
    99+
    2024-04-02
  • 数据结构TypeScript之二叉查找树实现详解
    目录树的结构特点面向对象方法封装二叉查找树(迭代版)二叉查找树的定义构造函数基本单元:二叉查找树节点主体:二叉查找树增加节点查找节点删除节点二叉树的遍历树的结构特点 树是一种有层次...
    99+
    2023-01-30
    TypeScript数据结构二叉查找树 TypeScript数据结构
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作