Python描述数据结构之线索二叉树篇

前言

  本篇章主要介绍线索二叉树,包括线索二叉树的基本概念、构造及遍历,并用Python实现其创建及其遍历等操作。

1. 基本概念

  上篇博客介绍的二叉链表的存储结构体现的只是一种父子关系,它不能直接得到结点在遍历过程中的前驱和后继信息。那既然不能直接得到,那就添加两个指针域ltagrtag来指向结点的前驱和后继,这样可以加快查找结点前驱和后继的速度,加上这两个指针域的二叉树就是线索二叉树啦!

l c h i l d lchild l t a g ltag d a t a data r t a g rtag r c h i l d rchild

  上面就是线索二叉树的结点,它是这样规定的:如果结点没有左子树,则令lchild指向其前驱结点;如果结点没有右子树,则令rchild指向其后继结点;ltagrtag两个标志域是用来标识指针域是指向左/右孩子还是指向前驱/后继结点。标志域的含义如下:
l t a g = { 0 , l c h i l d 1 , l c h i l d ltag=\begin{cases} 0, & lchild域指向结点的左孩子\\ 1, & lchild域指向结点的前驱 \end{cases} r t a g = { 0 , r c h i l d 1 , r c h i l d rtag=\begin{cases} 0, & rchild域指向结点的右孩子\\ 1, & rchild域指向结点的后继 \end{cases}

  ltagrtag这两个指针称为线索,上述的链表也称为线索链表

  二叉线索链表结点的定义如下:

class ThreadNode(object):
    def __init__(self, data='#'):
        self.data = data
        self.lchild = None
        self.rchild = None
        self.ltag = 0
        self.rtag = 0

2. 线索二叉树的构造

  以某种次序遍历将二叉树变为线索二叉树的过程称为线索化
  线索化的实质就是当二叉树中某结点不存在左孩子或右孩子时,将其lchild域或rchild域指向该结点的前驱或后继

在这里插入图片描述
在这里插入图片描述
  咱们就以这棵二叉树为例,来看一下线索二叉树是如何构造的。

2.1 先序线索二叉树

  上述二叉树的先序遍历为: A B D E C F ABDECF

在这里插入图片描述
  上面这棵就是先序线索二叉树,是通过先序遍历构造的,根据原二叉树的二叉链表可知,空指针(即度为1的结点或叶子结点的孩子指针域)得到了有效利用。
  先序线索二叉树寻找结点的后继的过程如下:
  (1) 如果该结点有左孩子,则左孩子就是该结点的后继;
  (2) 如果该结点无左孩子但有右孩子,则右孩子就是该结点的后继;
  (3) 如果该结点即无左孩子又无右孩子(即叶子结点),则右链域指向的结点就是该结点的后继。

2.2 后序线索二叉树

  上述二叉树的后序遍历为: D E B F C A DEBFCA

在这里插入图片描述
  后序线索二叉树寻找结点的后继的过程如下:
  (1) 如果该结点是二叉树的根,则该结点的后继为空;
  (2) 如果该结点是其双亲的右孩子,或者是其双亲的左孩子且双亲没有子树,则该结点的后继即为双亲;
  (3) 如果该结点是其双亲的左孩子,且其双亲有右子树,则该结点的后继为双亲的右子树上按后序遍历得到的第一个结点。
  这就有点复杂了,先序线索二叉树、后序线索二叉树寻找结点的前驱也都复杂,书上也没说老师也没讲,这里也就不介绍了,不常用,最常用的是下面要介绍的中序线索二叉树。

2.3 中序线索二叉树

  上述二叉树的后序遍历为: D B E A F C DBEAFC

在这里插入图片描述
  中序线索二叉树的建立如下:
  令指针PreNode指向刚刚访问过的结点,指针RootNode指向正在访问的结点,即PreNode指向RootNode的前驱。在中序遍历过程中,先判断RootNode是否有左孩子,若没有左孩子就将它的lchild指向PreNode;然后再判断PreNode是否有右孩子,若没有右孩子就将它的rchild指向RootNode
  为了方便,可以在二叉树的线索链表上添加一个头结点,令其lchild域的指针指向二叉树的根结点,其rchild域的指针指向中序遍历时访问的最后一个结点(即最右边的那个结点);然后再令二叉树中序序列中的第一个结点(即二叉树最左边的那个结点)的lchild域指针和最后一个结点的rchild域指针均指向头结点。没错,这是二叉树的双向线索链表。

在这里插入图片描述
  中序线索二叉树的创建代码如下:

    def CreateInThread(self):
        RootNode = self.__CreateBinaryTree()
        if RootNode is None:
            self.HeadNode.lchild = self.HeadNode
        else:
            # lchild域的指针指向二叉树的根结点
            self.HeadNode.lchild = RootNode
            self.PreNode = self.HeadNode
            self.InThread(RootNode)
            # 处理最后一个结点
            self.PreNode.rtag = 1
            self.PreNode.rchild = self.HeadNode
            # rchild域的指针指向中序遍历时访问的最后一个结点
            self.HeadNode.rchild = self.PreNode

    def InThread(self, TreeNode):
        if TreeNode is not None:
            # 递归, 线索化左子树
            self.InThread(TreeNode.lchild)
            if TreeNode.lchild is None:
                # 当前结点没有左孩子
                # 将当前结点的ltag置为1, 表示lchild域指向的是前驱
                TreeNode.ltag = 1
                TreeNode.lchild = self.PreNode
            if self.PreNode.rchild is None:
                # 前驱结点没有右孩子
                # 将前驱结点的rtag置为1, 表示rchild域指向的是后继, 即当前的TreeNode
                self.PreNode.rtag = 1
                self.PreNode.rchild = TreeNode
            # 标记刚刚访问的结点为下个结点的前驱结点
            self.PreNode = TreeNode
            self.InThread(TreeNode.rchild)

3. 中序线索二叉树的遍历

  中序线索二叉树寻找结点前驱的规律是:若ltag=1,则左链为线索,指示其前驱,否则遍历左子树中最后一个访问的结点(即左子树中最右边的那个结点)为其前驱。
  中序线索二叉树寻找结点后继的规律是:若rtag=1,则右链为线索,指示其后继,否则遍历右子树中第一个访问的结点(即右子树中最左边的那个结点)为其后继。
  中序线索二叉树的遍历代码实现如下:

    def InOrderThread(self):
        # TreeNode就是树的根结点
        TreeNode = self.HeadNode.lchild
        while TreeNode is not self.HeadNode:
            while TreeNode.ltag == 0:
                # 找到了树最左边的那个结点(不一定是叶结点)
                TreeNode = TreeNode.lchild
            self.VisitBinaryTreeNode(TreeNode)
            while TreeNode.rchild is not self.HeadNode and TreeNode.rtag == 1:
                # 线索后继
                TreeNode = TreeNode.rchild
                self.VisitBinaryTreeNode(TreeNode)
            # rtag=0就开始寻找右子树最左边那个结点
            TreeNode = TreeNode.rchild

4. 代码框架

class BinaryThreadTree(object):
    def __init__(self, data_list):
        self.data_list = data_list
        # 创建树的头结点
        self.HeadNode = ThreadNode()
        self.HeadNode.ltag = 0
        self.HeadNode.rtag = 1
        self.HeadNode.lchild = self.HeadNode
        self.PreNode = ThreadNode()

    def __CreateBinaryTree(self, root=None, pos=0):
        if pos >= len(self.data_list) or self.data_list[pos] == '#':
            # 递归结束条件
            return None
        else:
            root = ThreadNode(self.data_list[pos])
            # 递归建立左子树
            root.lchild = self.__CreateBinaryTree(root, 2*pos+1)
            # 递归建立右子树
            root.rchild = self.__CreateBinaryTree(root, 2*pos+2)
            return root

    def VisitBinaryTreeNode(self, RootNode):
        if RootNode.data != '#':
            print(RootNode.data, end=' ')

    def CreateInThread(self):

    def InThread(self, TreeNode):

    def InOrderThread(self):


if __name__ == '__main__':
    tree_obj = BinaryThreadTree('ABCDEF#')
    tree_obj.CreateInThread()
    tree_obj.InOrderThread()

  测试也是OK的:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42730750/article/details/108285846