本节介绍了线索二叉树的由来、构造过程图解、代码解析。
代码及解析如下:
class ThNode:#这个是线索化二叉树的结点类型,可以看到一共有5个域
def __init__(self,d=None):
self.data=d
self.ltag=0
self.rtag=0
self.lchild=None
self.rchild=None
class ThreadTree:#中序线索化二叉树类
def __init__(self,d=None):#因为线索二叉树涉及到某个域指向前驱结点或者某个域指向后继结点,因此要考虑如何设置先后关系,因此用两个相邻的指针来体现前后关系
self.b=None#指针b指向根节点
self.root=None#root指针就是最后那步添加的头结点,与一般的二叉树的头结点不同的那个头结点
self.pre=None#引入的新的指针,用于指向前驱结点
#注意下面用到的p和pre指针可以理解为所在的位置是*序遍历序列,实际上是在一个二叉树中进行运用
def CreateThread(self):#建立以root为头结点的中序线索二叉树
#先建立一个线索二叉树的头结点(有5个域的那个)
self.root=ThNode()
self.root.ltag=0
self.root.rtag=0
self.root.rchild=b#这个b是一般二叉树的头结点,但在最后要设置成*序序列的最后一个元素
#这里理应设置左孩子域的指向,但是考虑到一开始的初始化左孩子没有(一开始一般的二叉树是空树)的情况,让左孩子域指向本身。既然没有树了,那么右孩子也就设置为空了。
if self.b==None:
self.root.lchild=self.root
self.root.rchild=None
#如果下面仍然有树
else:
self.root.lchild=self.b#左孩子域指向原二叉树的根节点
self.pre=self.root#pre指向root
self._Thread(self.b)#对b进行线索化
self.pre.rchild=self.root
self.root.rchild=self.pre
def _Thread(self,p):#对p指向的结点线索化
#其实所谓线索化,就是检验每一个结点的左右孩子的指针是否为空,如果是空,就把tag域置为1,然后把这个空的指针域指向相应的前驱或者后继结点,如果不是空,就把相应的tag域置为0
if p!=None:
#先是递归,再是普通语句,然后是递归。这是中序遍历的方式。
self._Thread(p.lchild)
if p.lchild==None:#检验左孩子域
#注意:可以理解为这个p和pre指针在中序遍历序列中进行运用
p.lchild=self.pre#指向前驱结点
p.ltag=1
else:p.ltag=0
if self.pre.rchild==None:#检验右孩子域
self.pre.rchild=p
self.pre.rtag=1
else:self.pre.rtag=0
self.pre=p#pre后移一位,p在递归当中后移
self._Thread(p.rchild)
#遍历线索二叉树(以中序遍历为例)
#为什么这个东西要单拿出来讲呢?因为二叉树进行线索化之后他的指针发生了变化,导致之前先序中序后续遍历要根据结点的tag域进行调整
#但是我所要的结果是和普通的二叉树的遍历(先序中序后序)的遍历结果是一样的
#但是,在构造中序线索二叉树时,已经知道了中序遍历序列,现在我们只是假装不知道,然后去求中序遍历序列
#由于线索(之前孩子域为空现在设置为新节点的指针指向)是根据中序遍历序列得到的,所以可以从线索入手
def ThInOrder(self):
p=self.root.lchild#这样p才指向的原来二叉树的根节点
#这个算法没有用到递归,是因为用到了线索的规律
#有个规律:先找到根节点,不断的找右线索(因为右线索是后继),找到没有右线索了,换成右孩子,继续去找右线索
while p!=self.root:
#找到中序序列的开始结点(不断地找左孩子,最后一个肯定是开头)(中序遍历的特性)
while p!=self.root and p.ltag==0:
p=p.lchild
print(p.data,end=" ")
while p.ltag==1 and p.rchild!=self.root:#如果是线索就一直找下去然后输出
p=p.rchild
print(p.data,end=' ')
#如果不是线索就转向右子树
p=p.rchild#更新了p的值然后继续进行最外层的while循环