二叉树的遍历可以分为 深度优先 遍历和 广度优先 遍历。其中深度优先又可以分为前序遍历、中序遍历、后序遍历;广度优先又称为层次遍历
一、深度优先
所谓深度优先,即从根节点开始,沿着一条分支尽可能深的遍历分支上的所有节点。当一条分支上的节点遍历完成以后,回溯到父节点,对其他分支上的节点进行相同的遍历操作。
⒈ 前序遍历
根节点→左子树→右子树
① 递归实现
class Node:
def __init__(self, item):
self.item = item
self.left = None
self.right = None
def __repr__(self):
return str(self.__dict__)
def __str__(self):
return str(self.__dict__)
def pre_order(root):
if root is None:
return
print(root.item)
pre_order(root.left)
pre_order(root.right)
⓶ 非递归实现
通常,递归遍历容易理解,但对内存得消耗却非常厉害。所以这里介绍两种采用非递归方式实现的前序遍历
ⅰ采用堆栈方式实现,利用堆栈先进后出的特点,先遍历根节点以及左子树,然后将当前节点存入堆栈;当左子树遍历完成后,再从堆栈中弹出最后存入的节点,如果该节点存在右子树,则遍历右子树;如果不存在右子树,则继续从堆栈中然出节点。
def pre_order_non_recur(root):
stack = []
node = root
while node is not None or len(stack) > 0:
if node is not None:
print(node.item, sep=" ")
stack.append(node)
node = node.left
else:
node = stack.pop()
node = node.right
ⅱ以根节点为 current 节点
如果 current 节点的左子节点为空,则打印 current 的值,并将 current 指向其右子节点;
如果 current 节点的左子节点不为空,将其命名为 prev,如果 prev 的右子节点已经指向了 current 节点,则将 prev 的右子节点置为空,并将 current 指向 current 的游子节点;如果 prev 的右子节点为空,则打印 current 的值,并将 prev 的右子节点指向 current,将 current 指向 current 的左子节点;
循环以上操作知道 current 为空
def pre_order_non_recur_v2(root):
current = root
while current is not None:
if current.left is None:
print(current.item, end=" ")
current = current.right
else:
prev = current.left
while prev.right is not None and prev.right != current:
prev = prev.right
if prev.right is None:
print(current.item, end=" ")
prev.right = current
current = current.left
else:
prev.right = None
current = current.right
⒉ 中序遍历
左子树→根节点→右子树
① 递归实现
def in_order(root):
if root is None:
return
in_order(root.left)
print(root.item)
in_order(root.right)
⓶ 非递归实现
ⅰ 堆栈方式实现
def in_order_non_recur(root):
stack = []
node = root
while node is not None or len(stack) > 0:
if node is not None:
stack.append(node)
node = node.left
else:
node = stack.pop()
print(node.item, sep=" ")
node = node.right
ⅱ 此方法与前序遍历的第二种非递归实现方法原理相同,此处不再赘述
def in_order_non_recur_v3(root):
current = root
while current is not None:
if current.left is None:
print(current.item, end=" ")
current = current.right
else:
prev = current.left
while prev.right is not None and prev.right != current:
prev = prev.right
if prev.right is None:
prev.right = current
current = current.left
else:
prev.right = None
print(current.item, end=" ")
current = current.right
⒊ 后序遍历
左子树→右子树→根节点
① 递归实现
def post_order(root):
if root is None:
return
post_order(root.left)
post_order(root.right)
print(root.item)
⓶ 非递归实现
ⅰ 使用两个堆栈实现,此种方法实现起来相对简单,理解起来也相对容易
def post_order_non_recur(root):
stack_first = []
stack_second = []
stack_first.append(root)
while len(stack_first) > 0:
node = stack_first.pop()
stack_second.append(node)
if node.left is not None:
stack_first.append(node.left)
if node.right is not None:
stack_first.append(node.right)
while len(stack_second) > 0:
node = stack_second.pop()
print(node.item, end=" ")
ⅱ只用一个堆栈实现
def post_order_non_recur_v2(root):
"""
将二叉树按照 右子树、根节点、左子树 的顺序入栈
然后再依次出栈,如果弹出的节点为左子树,则正常输出;如果弹出的节点为根节点,则同时弹出右子树,并将根节点重新入栈,然后对根节点的右子树重复上述操作,知道堆栈为空
"""
stack = []
while True:
while root is not None:
if root.right is not None:
stack.append(root.right)
stack.append(root)
root = root.left
root = stack.pop()
if root.right is not None and len(stack) > 0 and stack[-1] == root.right:
stack.pop()
stack.append(root)
root = root.right
else:
print(root.item, end=" ")
root = None
if len(stack) <= 0:
break
def post_order_non_recur_v3(root):
"""
此方法与前述方法原理相同,不同之处在于此方法将 根节点 两次入栈,代替了前述方法中将右子树和根节点相继入栈的操作
扎样,在后续操作中避免了将右子树出栈然后将根节点再次入栈
"""
stack = []
while True:
while root is not None:
stack.append(root)
stack.append(root)
root = root.left
if len(stack) <= 0:
break
root = stack.pop()
if len(stack) > 0 and stack[-1] == root:
root = root.right
else:
print(root.item, end=" ")
root = None
ⅲ 使用集合实现
def post_order_non_recur_v4(root):
"""
从根节点开始按照 左子树→右子树的数序遍历二叉树的节点,已经打印输出的节点放入集合 visited
每打印输出一个节点,则重新从根节点开始重复上述操作
"""
temp = root
visited = set()
while temp is not None and temp not in visited:
if temp.left is not None and temp.left not in visited:
temp = temp.left
elif temp.right is not None and temp.right not in visited:
temp = temp.right
else:
print(temp.item, end=" ")
visited.add(temp)
temp = root
二、 广度优先
所谓广度优先,即从根节点开始,按照深度递增的顺序,遍历完当前深度的所有节点后再遍历下一个深度的所有节点。
以下代码实现利用了队列先进先出的特点。
def level_order(root):
if root is None:
return root
queue = []
queue.append(root)
while len(queue) > 0:
root = queue.pop(0)
print(root.item, end=" ")
if root.left is not None:
queue.append(root.left)
if root.right is not None:
queue.append(root.right)