二叉树遍历,在面试中出现的概率极大,常常需要面试者掌握二叉树的三种遍历方式,前序遍历、中序遍历、后序遍历,同时又要要求面试者掌握递归和非递归实现。
前序遍历、中序遍历、后序遍历三个遍历中,递归的实现方式最简单,而且有规律可循。
三种变量的非递归实现较难。
三个遍历方式的定义分别是:
- 前序遍历:先访问根节点,再前序遍历左子树,再前序遍历右子树
- 中序遍历:先中序遍历左子树,再访问根节点,再中序遍历右子树
- 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问根节点
Tip:以根节点的访问顺序作为前中后遍历的定义,且三种遍历方式都是先遍历左子树,再遍历右子树。**
本节将会教你用一套模板实现三种递归方式,面试不再愁!!!
前序遍历
递归
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
非递归
- 模板法
先记住先序遍历,中序和后序遍历只需要在此基础上修改即可
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if root is None:
return []
result = [] # 保存结果
stack = [root] # 压入 root 节点
while stack:
p = stack.pop() # 访问过的节点弹出
if p is None: # 空节点表示之前已经访问过了,现在需要处理除了递归之外的内容
p = stack.pop() # 彻底从栈中移除
result.append(p.val)
else:
if p.right: # 右节点先压栈,最后处理
stack.append(p.right)
if p.left:
stack.append(p.left)
stack.append(p) # 当前节点重新压栈(留着以后处理),因为先序遍历所以最后压栈
stack.append(None) # 在当前节点之前加入一个空节点表示已经访问过了
return result
- 非模板
前序遍历的非递归实现比较简单
建立一个栈 stack,先把 root 节点压入,然后 while 判断 stack 是否为空作为循环退出的条件
进入 while 循环以后,弹出栈顶的节点,若该节点有右子节点、左子节点的话,压入栈中(注意:压入栈的时候先压入右子节点,再压入左子节点,这是因为栈是后入先出,左子节点最后被压入,但是最先被弹出)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
result = []
while stack:
node = stack.pop()
if node:
result.append(node.val)
if node.right:
stack.append(node.right) # 先压入右边
if node.left:
stack.append(node.left) # 后压入左边
return result
中序遍历
递归
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
时间复杂度:O(n)。
空间复杂度:最坏情况下需要空间O(n),平均情况为 O(logn)。
非递归
- 模板法
```python
Definition for a binary tree node.
class TreeNode:
def init(self, x):
self.val = x
self.left = None
self.right = None
class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: if root is None: return [] result = [] stack = [root] while stack: p = stack.pop() if p is None: p = stack.pop() result.append(p.val) else: if p.right: # 先压入右边 stack.append(p.right) stack.append(p) stack.append(None) if p.left: stack.append(p.left) return result
- 非模板
> 创建一个栈 stack,一个 cur 变量,用来记录当前的节点,把 cur 或 stack 非空作为循环的条件,进入循环,判断 cur 节点是否为空,非空的话,往复变量 当前节点的左子节点,压入栈中。
```python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [] # 最开始不压入 root 节点
result = []
cur = root #
while cur or stack: # 或
while cur: # 不断把当前节点的 左子节点压入
stack.append(cur)
cur = cur.left
cur = stack.pop() # 弹出
result.append(cur.val)
cur = cur.right
return result
后序遍历
递归
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
非递归
- 模板法
```python
Definition for a binary tree node.
class TreeNode:
def init(self, x):
self.val = x
self.left = None
self.right = None
class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: if root is None: return [] result = [] stack = [root] while stack: p = stack.pop() if p is None: p = stack.pop() result.append(p.val) else: stack.append(p) stack.append(None) if p.right: stack.append(p.right) if p.left: stack.append(p.left) return result
- 非模板<br />
> 后序遍历弹出(输出值)的条件是**当前节点是叶节点或者子节点也访问过了**
```python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = [root]
result = []
p = root
while stack:
top = stack[-1]
# 如果是叶子结点,儿子也已经有访问过,则输出它,并记录下当前输出结点
if (top.left == p) or (top.right == p) or \
(not top.left and not top.right): # 叶子元素
p = stack.pop()
result.append(p.val)
# 如果它不是叶子结点,儿子也没有访问,那么就需要将它的右儿子,左儿子压入。
else:
# 先访问的后加入
if top.right: # 先加右
stack.append(top.right)
if top.left: # 再加左
stack.append(top.left)
return result
口诀
创建 result 保存结果
创建 stack 压入 root
while 循环判断 stack
首先 p=stack.pop()
p 为空,p=stack.pop(),result.append(p.val)
p 非空,按照题意压 p, p.right, p.left
压入 p 之后压入 None,标记已经被访问
参考
- https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
- https://github.com/azl397985856/leetcode/blob/master/problems/144.binary-tree-preorder-traversal.md
- https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
- https://github.com/azl397985856/leetcode/blob/master/problems/94.binary-tree-inorder-traversal.md
- https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
- https://github.com/azl397985856/leetcode/blob/master/problems/145.binary-tree-postorder-traversal.md