二叉树的先序遍历的迭代形式
二叉树中序遍历的迭代形式
class BinNode(object):#二叉树的基本组成单位是二叉树中的节点
def __init__(self,e):
self.data=e
self.parent=None
self.lChild=None
self.rChild=None
self.height=0#记录二叉树中当前节点的高度
def size(self):
# 返回包含当前节点在内的所有后代节点的总数,递归统计
# 统计当前节点具有多少个后代节点数必须遍历树中的每个节点
s=1
if self.lChild is not None:
s+=self.lChild.size()
if self.rChild is not None:
s+=self.rChild.size()
return s
def insertAsLC(self,e):# 将某一个具体的数值生成一个新节点,并且作为当前节点的做孩子插入树中
new_node=BinNode(e)
new_node.parent=self
self.lChild=new_node# 将当前节点的左孩子引用给新节点
self.height+=1
def insertAsRC(self,e):# 根据数值e产生新节点,作为当前节点的右孩子插入到二叉树中
new_node = BinNode(e)
new_node.parent = self
self.rChild = new_node
self.height+=1
def succ(self,node):# 中序遍历意义下当前节点的直接后继
return
class BinTree(object):
def __init__(self,e=0):
self.root=BinNode(e)
self.size=1 # 当前二叉树中的节点总数,即树的规模
self.height=0
# 只包含一个根节点的树,高度为0,不包含任何节点的树的高度为-1
# 可以将二叉树的高度理解成是从根节点出发到所有叶子节点的路径最大值
# 路径指的是边长而不是节点数
self.output=[]# 用于记录输出的序列(遍历的序列)
def updateHeight(self,node):
# 只更新节点node的高度
if node.lChild is not None and node.rChild is not None:
node.height=1+max(node.lChild.height,node.rChild.height)
elif node.lChild is None and node.rChild is not None:
node.height=1+node.rChild.height
elif node.rChild is None and node.lChild is not None:
node.height = 1 + node.lChild.height
else:
node.height=0
def updateHeightAbove(self,node):
# 更新从当前节点开始以及其所有父代节点的高度
while(node is not None):# 不断回溯node节点的父代节点
self.updateHeight(node)
node=node.parent
def insertAsRC(self,node,e):
self.size+=1
node.insertAsRC(e)
self.updateHeightAbove(node)
return node.rChild
def insertAsLC(self,node,e):
self.size+=1
node.insertAsLC(e)
self.updateHeightAbove(node)
return node.lChild
def output_seq(self):
self.output=[]
def visit(self,data):
self.output.append(data)
def preOrder(self,node):
# 实现二叉树的先序遍历
# node 表示当前所遍历的子树的根节点
if node is None:# 递归基,很重要,是递归函数的关键步骤
return
print(node.data)
self.preOrder(node.lChild)
self.preOrder(node.rChild)
# 由于对于二叉树的三种遍历方式本身就是以递归的形式定义的,故而递归地进行遍历代码形式很简单
# 递归调用的语句出现在递归函数的末尾,这种递归调用称为尾递归,尾递归很容易通过栈改写成为迭代形式
def preOrder_iteration(self,node):# 改写成迭代形式的先序遍历
if node is None:
return
stack=[node]# 用列表模拟栈,列表左端为栈顶,弹出元素
while(len(stack)>0):
temp_node=stack.pop(-1)# 弹出栈顶元素
print(temp_node.data)
if temp_node.rChild is not None:
stack.append(temp_node.rChild)
if temp_node.lChild is not None:
stack.append(temp_node.lChild)
return
def visitAlongLeftBranch(self,node,stack=[]):
# 以输入的节点作为当前子树的根节点,访问当前子树左侧链上的各个节点
if node is None:
return
# 辅助栈,用来依次存储子树根节点左侧链上各个节点的右子树根节点
while(node):
print(node.data)
if node.rChild is not None:
stack.append(node.rChild)
node=node.lChild
def preOrder_iteration2(self,node,stack=[]):
if node is None:
return
'''
对于二叉树中的任意一颗子树,一旦该子树的根节点接过控制权并开始访问
都是从根节点沿着左孩子的分支不断访问,直到当前节点没有左孩子,然后根据当前节点回溯,找到应该将节点的访问控制权交给哪一个节点
当节点接过访问控制权之后,又重复着同样的访问操作(沿着左孩子的分支一直向下访问)
定义:将起始于树的根节点的接下来总是沿着左侧孩子分支不断下行的链称为左侧链
整个过程分为两个部分:首先是自顶向下地依次访问左侧链上的沿途节点,再自底而上地依次遍历各个层上的每一棵右子树
'''
self.visitAlongLeftBranch(node,stack)
while(stack):# 对于树根节点左侧链的每个节点右子树的根节点
self.visitAlongLeftBranch(stack.pop(-1),stack)
# stack 栈是对于所有右子树公用的栈,并且弹出顺序按照最开始的插入顺序进行
def Inorder_recursion(self,node):
'''
递归形式 实现对以node为根节点的子树进行中序遍历 先中序遍历左子树,再访问根节点,最后中序遍历右子树
:param node:
:return:
'''
if node==None:# 处理递归基
return
if node.lChild is not None:
self.Inorder_recursion(node.lChild)
print(node.data)
if node.rChild is not None:
self.Inorder_recursion(node.rChild)
def Inorder_iteration(self,node):
'''
迭代形式,对以node为根节点的子树进行中序遍历
迭代就是一种使用循环来代替递归的结构
:param node:
:return:
中序遍历过程中,最先接受访问的节点是根节点左侧链的末端节点
节点访问的控制权由树的根节点沿着左侧链逐渐转让该后代的左孩子节点
直到到达左侧链的叶子结点,它没有左孩子,无法继续转让节点的控制权,故而到达它的访问时机
于是访问当前节点,当前节点再将节点的访问控制权交给当前节点的右孩子,以右孩子为根节点,继续上述过程
即:从根节点开始,一直沿着左侧分支逐层向下,直到末端不能再向下的那个节点 左侧分支(left branch)
整个二叉树可以分解为一条起自根节点的左侧链,左侧链上的每个节点都具有右孩子以及以右孩子为根节点的右子树
则对于左侧链中的任何一个节点(对应于二叉树中的任何一个局部),将左侧链上的节点分别记作 L(0) L(1) ……L(k)其中L(0)表示树的根节点
当访问控制权被交到L(k-1)节点时,并不是立刻访问L(k-1)节点,它会将节点的控制权交给它的左孩子,直到它的左孩子(记作L(k))接受访问之后,
才会深入到它的左孩子的右子树中,在右子树中进行完全相同的操作,即以右子树根节点为前文所述的根节点,沿着根节点的左侧链向下寻找……
直到右子树中的所有节点都被遍历完成后(既可以认为他们被访问完毕,也可以等效地认为它们从来就没有存在过),
控制权才会重新的交给L(k)的parent节点(左侧链中的上层节点),此时访问parent节点,然后将节点的访问控制权交给parent节点的右子树
以右子树的根节点为起始点,依次将左侧链中的节点入栈……
将对整个二叉树进行中序遍历的过程划分成N个阶段,N等于二叉树左侧链上的节点数(包括二叉树的根节点),在左侧链中的每个节点的情节完全相同:
访问左侧链上的当前节点,再遍历当前节点的右子树,再回到左侧链中的上一级节点继续访问
从根节点出发沿左分支下行,直到最深的节点,它就是全局首先被访问的节点
'''
stack=[]
while(node):
stack.append(node)
node=node.lChild
# stack 中存放的是二叉树左侧链中的所有节点
while(stack):
temp_node=stack.pop(-1)# 弹出栈顶元素,即左侧链的末端节点
print(temp_node.data)
temp_node = temp_node.rChild# 将右子树的根节点放入栈中
if temp_node is not None:
stack.append(temp_node)
# 再依次将右子树根节点的左孩子节点放入栈中
while(temp_node.lChild):
stack.append(temp_node.lChild)
temp_node=temp_node.lChild
return
def posOrder_recursion(self,node):
# 递归版本的后序遍历实现 先后序遍历左子树,再后序遍历右子树,最后访问根节点
if node==None:
return
if node.lChild:
self.posOrder_recursion(node.lChild)
if node.rChild:
self.posOrder_recursion(node.rChild)
print(node.data)
return
def midOrder(self,node):
# 二叉树的层序遍历
# 自上而下访问各个深度的节点,同样深度的节点中自左向右
# 先进先出,使用的数据结构是:队列
if node==None:
return
queue=[node]
result=[]
while(queue):
temp_node=queue.pop(-1)# 列表的头部为队列首部,列表的尾部为队列尾部
result.append(temp_node.data)
if temp_node.lChild:
queue.insert(0,temp_node.lChild)
if temp_node.rChild:
queue.insert(0,temp_node.rChild)
# print(result)
return result
# 二叉树的重构,根据对于二叉树的遍历序列,重构原始的二叉树
# 二叉树的三种遍历方式以及层序遍历,最终得到的遍历序列长度相同,只是对于原始二叉树中节点数值的访问顺序不同而已
if __name__=="__main__":
tree=BinTree(0)
tree.insertAsLC(tree.root,1)
tree.insertAsRC(tree.root, 2)
tree.insertAsLC(tree.root.lChild,3)
tree.insertAsRC(tree.root.lChild, 4)
tree.insertAsLC(tree.root.rChild, 5)
tree.insertAsRC(tree.root.rChild, 6)
# tree.preOrder(tree.root)
# tree.Inorder_iteration(tree.root)
tree.posOrder_recursion(tree.root)
print(tree.midOrder(tree.root))
# print(tree.root.height)
# print(tree.root.lChild.height)
'''
所构建的二叉树
0
/ \
1 2
/ \ / \
3 4 5 6
二叉树先序遍历的结果为 0 1 3 4 2 5 6
其中4个叶子结点的高度为0,根节点的高度为2,左右子树根节点的高度为1
节点的高度可以理解成从该节点到二叉树根节点的唯一路径的长度(边的长度)
二叉树中序遍历的结果为 3 1 4 0 5 2 6
二叉树后序遍历的结果为 3 4 1 5 6 2 0
'''