二叉树,一个有穷的结点集合。这个集合能够为空,若是不为空,则它是由根结点和称其为左子树和右子树的两个不相交的二叉树组成。node
二叉树有五种基本形态:python
彻底二叉树可使用顺序存储结构,按从上至下,从左到右顺序存储,若是一颗彻底二叉树以下:算法
通常二叉树也可使用顺序存储,只是会形成空间的浪费。数组
因为通常的二叉树使用顺序存储结构,容易形成空间的浪费,所以可使用链式存储。其结构以下app
class TreeNode:
def __init__(self, x, left=None, right=None):
self.val = x # 值
self.left = left # 左孩子
self.right = right # 右孩子
复制代码
因为二叉树不是线性结构,所以它的遍历也就不像数组或者链表那么简单,它须要沿着某条搜索路线,依次对树中每一个结点均作一次且仅作一次访问。post
前序遍历的遍历过程:spa
使用递归的方式实现以下:3d
def pre_order_traversal(root: TreeNode):
if root:
print(root.val)
pre_order_traversal(root.left)
pre_order_traversal(root.right)
复制代码
中序遍历的遍历过程:code
递归代码cdn
def in_order_traversal(root: TreeNode):
if root:
in_order_traversal(root.left)
print(root.val)
in_order_traversal(root.right)
复制代码
后序遍历的遍历过程:
递归代码
def post_order_traversal(root: TreeNode):
if root:
post_order_traversal(root.left)
post_order_traversal(root.right)
print(root.val)
复制代码
从上面三幅图的访问路径,能够看出,不管是前,中,后序遍历,在遍历过程当中通过结点的路线都是同样的,只是访问各结点的时机不一样
下图使用ⓧ,☆,△三种符号分别标记出,前序,中序,后序访问各结点的时刻
使用递归的方式能够比较容易的写出遍历算法,那么若是不用递归呢?
咱们知道,递归的实现须要借助栈,那么能够用栈+循环的方式来实现遍历算法。
以中序遍历为列:
仍然以上面的二叉树为例,来看看非递归的中序遍历的运行过程:
def in_order_traversal(root: TreeNode):
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
node = stack.pop()
print(node.val)
root = node.right
复制代码
中序遍历是在第二次通过结点的时候,才访问该结点的,所以参照中序遍历的非递归算法,把print语句移到第一次通过结点时,就访问该结点,那么非递归前序遍历的实现也就出来了。
def pre_order_traversal(root: TreeNode):
stack = []
while root or stack:
while root:
stack.append(root)
print(node.val)
root = root.left
if stack:
node = stack.pop()
root = node.right
复制代码
非递归后序遍历比较复杂,并且实现的方式也有多种,这里提供一个比较好理解的标记法。根据后序遍历的定义,要先访问完左子树,再访问完右子树,最后才访问根结点,那么仍是套以前的代码结构,可是作个标记,在弹出元素的时候,判断是否有右结点或者右结点是否被访问过,若是知足则访问该结点,不知足就将它再次压回栈中,并转向它的右结点
def post_order_traversal(root: TreeNode):
stack = []
visited_node = None # 前一个被访问的结点
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
node = stack.pop()
if not node.right or node.right == visited_node:
# 没有右孩子或者右孩子已经被访问了,才访问该结点
print(node.val)
visited_node = node
else:
# 不然就将该结点从新压回栈了,并转向它的右结点
stack.append(node)
root = node.right
复制代码
二叉树的遍历,除了上面三种以外,还有一种层次遍历,即一层一层的访问
算法实现能够借助队列实现,先根结点入队,而后:
def level_order_traversal(root: TreeNode):
queue = [root]
while queue:
node = queue.pop(0)
print(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
复制代码
实际上二叉树的遍历核心问题:二维结构的线性化,当访问一个结点的时候,还须要访问其左右结点,可是访问左结点以后,若是再返回访问其右结点? 所以须要一个存储结构保存暂时不访问的结点,那么存储结构能够为栈,或者队列,对应的就有前,中,后,层次遍历的出现。
二叉树的遍历有许多应用,好比:输出二叉树中的叶子结点,求二叉树的高度等等,所以遍历对二叉树来讲是十分重要的。
Thanks!