树的遍历是树的一种重要的运算。所谓遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。那么树的两种重要的遍历模式是深度优先遍历和广度优先遍历,深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。
从树的root开始,从上到下从从左到右遍历整个树的节点
class Node(object):
"""节点类"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class Tree(object):
"""树类"""
def __init__(self, root=None):
self.root = root
def add(self, elem):
"""为树添加节点"""
node = Node(elem)
#如果树是空的,则对根节点赋值
if self.root == None:
self.root = node
else:
queue = []
queue.append(self.root)
#对已有的节点进行层次遍历
while queue:
#弹出队列的第一个元素
cur = queue.pop(0)
if cur.lchild == None:
cur.lchild = node
return
elif cur.rchild == None:
cur.rchild = node
return
else:
#如果左右子树都不为空,加入队列继续判断
queue.append(cur.lchild)
queue.append(cur.rchild)
def breadth_travel(self, root):
"""利用队列实现树的层次遍历"""
if root == None:
return
queue = []
queue.append(root)
while queue:
node = queue.pop(0)
print node.elem,
if node.lchild != None:
queue.append(node.lchild)
if node.rchild != None:
queue.append(node.rchild)
对于一颗二叉树,深度优先搜索是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
那么深度遍历有重要的三种方法。这三种方式常被用于访问树的节点,它们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)
简单的来说就像下图一样
上图所示:
先序遍历的顺序为:0,1,2
中序遍历的顺序为:1,0,2
后序遍历的顺序为:1,2,0
那么结合代码来看一下,就非常清楚了
class Node(object):
"""节点类"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class Tree(object):
"""树类"""
def __init__(self, root=None):
self.root = root
def add(self, elem):
"""为树添加节点"""
node = Node(elem)
#如果树是空的,则对根节点赋值
if self.root == None:
self.root = node
else:
queue = []
queue.append(self.root)
#对已有的节点进行层次遍历
while queue:
#弹出队列的第一个元素
cur = queue.pop(0)
if cur.lchild == None:
cur.lchild = node
return
elif cur.rchild == None:
cur.rchild = node
return
else:
#如果左右子树都不为空,加入队列继续判断
queue.append(cur.lchild)
queue.append(cur.rchild)
def breadth_travel(self, root):
"""利用队列实现树的层次遍历"""
if root == None:
return
queue = []
queue.append(root)
while queue:
node = queue.pop(0)
print node.elem,
if node.lchild != None:
queue.append(node.lchild)
if node.rchild != None:
queue.append(node.rchild)
def preorder(self, root):
"""递归实现先序遍历"""
if root == None:
return
print root.elem
self.preorder(root.lchild)
self.preorder(root.rchild)
def inorder(self, root):
"""递归实现中序遍历"""
if root == None:
return
self.inorder(root.lchild)
print root.elem
self.inorder(root.rchild)
def postorder(self, root):
"""递归实现后续遍历"""
if root == None:
return
self.postorder(root.lchild)
self.postorder(root.rchild)
print root.elem
上面是递归的写法,那下面我们来讨论一下非递归又该怎么去实现二叉树的前中后序的遍历呢?
三种方法都很巧妙
详细看代码示例
class Node(object):
"""二叉树节点"""
def __init__(self, item):
self.elem = item
self.lchild = None
self.rchild = None
class Tree(object):
"""二叉树"""
def __init__(self):
self.root = None
def add(self, item):
node = Node(item)
if self.root is None:
self.root = node
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
if cur_node.lchild is None:
cur_node.lchild = node
return
else:
queue.append(cur_node.lchild)
if cur_node.rchild is None:
cur_node.rchild = node
return
else:
queue.append(cur_node.rchild)
def breadth_travel(self):
"""广度遍历(层次遍历)"""
if self.root is None:
return
queue = [self.root]
while queue:
cur_node = queue.pop(0)
print(cur_node.elem, end=" ")
if cur_node.lchild is not None:
queue.append(cur_node.lchild)
if cur_node.rchild is not None:
queue.append(cur_node.rchild)
def preorder(self, node):
"""先序遍历"""
if node is None:
return
print(node.elem,end=" ")
self.preorder(node.lchild)
self.preorder(node.rchild)
def inorder(self, node):
"""中序遍历"""
if node is None:
return
self.inorder(node.lchild)
print(node.elem, end=" ")
self.inorder(node.rchild)
def postorder(self, node):
"""后序遍历"""
if node is None:
return
self.postorder(node.lchild)
self.postorder(node.rchild)
print(node.elem, end=" ")
def preOrderUnRecur(self,node):
"""
## 先序遍历非递归方式实现
原理是利用一个栈,先压入根节点,然后当栈不为空的时候,循环执行以下过程:
1. 先从栈中弹出一个元素,并打印
2. 然后判断右子树为不为空,不为空,将右子树压入栈中
3. 然后判断左子树为不为空,不为空,将左子树压入栈中
因为栈是先进后出的 所以是 先右子树进栈,再左子树,即 (右,左) 的顺序。那么出栈的时候就是,先打印中,然后出左,再出右,一直循环,从而实现先序遍历(中左右)
"""
if node is None:
return
stack = [] #定义一个栈
stack.append(node)
while len(stack):
pop_node = stack.pop()
print(pop_node.elem, end=" ")
if pop_node.rchild is not None:
stack.append(pop_node.rchild)
if pop_node.lchild is not None:
stack.append(pop_node.lchild)
print("")
def inOrderUnRecur(self, node):
"""
## 中序遍历非递归方式实现
"""
if node is None:
return
stack = [] #定义一个栈
while len(stack) or node is not None: #当根节点不为空或者栈不为空的时候进入
if node is not None: #当前节点不为空,当前节点入栈,当前节点往左子树移动 导致一次就压入左边的一整个边线 所以 出栈的时候 是左中右
stack.append(node)
node = node.lchild
else: # 当前节点为空,从栈中拿出一个,打印,当前节点往右子树移动
node = stack.pop()
print(node.elem,end=" ")
node = node.rchild
print("")
def posOrderUnRecur(self, node):
"""
## 后序遍历非递归方式实现
原理是利用两个栈,先压入根节点,然后当栈不为空的时候,循环执行以下过程:
1. 先从第一个栈中弹出一个元素,压入第二个栈(中)
2. 然后判断左子树为不为空,不为空,将左子树压入栈中
3. 然后判断右子树为不为空,不为空,将右子树压入栈中
先压左 ,再压右 后面出来就是右,左 ,压入到第二个栈就是 (左,右)
从而,第二个栈的栈里存的是中右左,出栈就是(左右中) 实现后序遍历
"""
if node is None:
return
stack1 = [] # 定义第一个栈
stack2 = [] # 定义第二个栈
stack1.append(node)
while len(stack1):
pop_node = stack1.pop()
stack2.append(pop_node) # 参照先序遍历的时候是拿出来打印的,现在压到另一个栈中, 压入第二个栈的是(中)
if pop_node.lchild is not None: #然后先压左 ,再压右 后面出来就是右,左 ,压入到第二个栈就是 (左,右)
stack1.append(pop_node.lchild)
if pop_node.rchild is not None:
stack1.append(pop_node.rchild)
while len(stack2):
pop2_node = stack2.pop()
print(pop2_node.elem, end=" ")
print("")
if __name__ == '__main__':
tree = Tree()
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
tree.add(6)
tree.add(7)
tree.breadth_travel()
print(" ")
tree.preorder(tree.root) # 递归方式: 1 2 4 5 3 6 7
print(" ")
tree.preOrderUnRecur(tree.root) # 非递归方式: 1 2 4 5 3 6 7
tree.inorder(tree.root) # 递归方式: 4 2 5 1 6 3 7
print(" ")
tree.inOrderUnRecur(tree.root) # 非递归方式: 4 2 5 1 6 3 7
tree.postorder(tree.root) #递归方式: 4 5 2 6 7 3 1
print(" ")
tree.posOrderUnRecur(tree.root) # 非pos递归方式: 4 5 2 6 7 3 1
至此,二叉树的遍历就讲完全了,可以轻松解决面试题如:
题:实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式