遍历二叉树是以一定的规则(顺序)将二叉树中结点排列成一个线性排列,结点在这个排列中有唯一一个前趋和唯一一个后继(第一个结点没有前驱,最后一个结点没有后继)。在有n个结点的二叉树中必有n+1个空的链域,不加以利用的话有些浪费,如果我们用它们存放结点在遍历中的前驱和后继信息,也就是对它们线索化,线索化之后的树再对其进行遍历时,可以提升遍历效率。
像上面所说,二叉树的分支要么指向它的孩子,要么指向它在遍历中的前驱/后继,为了加以区分,需要给二叉树的结点增加一个flag来指示分支是指向它的孩子还是它在遍历中的前驱/后继。
中序遍历的线索化:
中序遍历二叉树,设置prev指向前一个访问的结点,curr指向当前访问的结点,若prev结点没有右孩子,则设置它的righttag为1,并让rightchild为curr,表示prev结点的后继为curr,若curr结点没有左孩子,则设置它的lefttag为1,并让leftchild为prev,表示curr结点的前驱为prev。
中序线索化二叉树的遍历:
1)找到子树遍历中的第一个结点(树的最左下结点),访问它
2)访问当前结点的righttag,若它指示rightchild是它的后继,当前结点更新为它的后继,访问它,重复2)直到当前结点的righttag指示rightchild是它的右孩子,则当前结点更新为它的右孩子,转到1
class Node:
leftchld = None
rightchld = None
lefttag = 0
righttag = 0
def __init__(self, val = "null"):
self.val = val
def __str__(self):
return self.val
class InorderThreadedTree:
def __init__(self, root):
self.root = Node("X")
self.root.leftchld = root
self.root.righttag = 1
self.threading()
def _traverse(self, root):
if not root:
return
yield from self._traverse(root.leftchld)
yield root
yield from self._traverse(root.rightchld)
def threading(self):
prev = self.root
_iter = self._traverse(self.root.leftchld)
curr = next(_iter)
curr.leftchld = prev
curr.lefttag = 1
#print("%s prev %s" % (curr, prev))
prev = curr
for curr in _iter:
if not prev.rightchld:
prev.rightchld = curr
prev.righttag = 1
#print("%s next %s" % (prev, curr))
if not curr.leftchld:
curr.leftchld = prev
curr.lefttag = 1
#print("%s prev %s" % (curr, prev))
prev = curr
curr.rightchld = self.root
curr.righttag = 1
#print("%s next %s" % (curr, self.root))
self.root.rightchld = curr
self.root.righttag = 1
#print("%s next %s" % (self.root, curr))
def traverse(self):
root = self.root
curr = root.leftchld
while curr is not root:
while not curr.lefttag:
curr = curr.leftchld
yield curr
while curr.righttag and curr.rightchld is not root:
curr = curr.rightchld
yield curr
curr = curr.rightchld
def reverse(self):
root = self.root
curr = root.rightchld
yield curr
while True:
while curr.lefttag and curr.leftchld is not root:
curr = curr.leftchld
yield curr
curr = curr.leftchld
if curr is root:
break
while not curr.righttag:
curr = curr.rightchld
yield curr
def create_tree(treestr):
cursor = 0
def getval():
nonlocal cursor
start = cursor
while cursor < len(treestr):
if treestr[cursor] in ('(', ')', ','):
break
cursor += 1
return treestr[start:cursor].strip()
def create():
nonlocal cursor
val = getval()
print(val)
if not val:
return None
root = Node(val)
#one node tree
if cursor >= len(treestr):
return root
#leaf node
if treestr[cursor] in (',', ')'):
return root
if treestr[cursor] == '(':
cursor += 1
root.leftchld = create()
if treestr[cursor] == ',':
cursor += 1
root.rightchld = create()
if treestr[cursor] == ')':
cursor += 1
return root
return create()
root = create_tree("A(B(C(,D),E),F(,G))")
#print(root)
tree = InorderThreadedTree(root)
print(" ".join([str(i) for i in tree.traverse()]))
print(" ".join([str(i) for i in tree.reverse()]))
前序/后序遍历的线索化及其线索化之后的遍历,也有类似的逻辑,这里不再赘述。
感觉树的线索化没有什么价值,一旦树的结构发生变化,就要对树重新进行线索化,还有我们完全可以副设一个list记录它的遍历序列,不是更方面更高效?