二叉树遍历,在面试中出现的概率极大,常常需要面试者掌握二叉树的三种遍历方式,前序遍历、中序遍历、后序遍历,同时又要要求面试者掌握递归和非递归实现。
前序遍历、中序遍历、后序遍历三个遍历中,递归的实现方式最简单,而且有规律可循。
三种变量的非递归实现较难。
三个遍历方式的定义分别是:
- 前序遍历:先访问根节点,再前序遍历左子树,再前序遍历右子树
- 中序遍历:先中序遍历左子树,再访问根节点,再中序遍历右子树
- 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问根节点
Tip:以根节点的访问顺序作为前中后遍历的定义,且三种遍历方式都是先遍历左子树,再遍历右子树。**
本节将会教你用一套模板实现三种递归方式,面试不再愁!!!
前序遍历
递归
# Definition for a binary tree node.# class TreeNode:# def __init__(self, x):# self.val = x# self.left = None# self.right = Noneclass 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 = Noneclass 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 = Noneclass 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 = Noneclass 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 = Noneclass 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.leftcur = stack.pop() # 弹出result.append(cur.val)cur = cur.rightreturn result
后序遍历
递归
# Definition for a binary tree node.# class TreeNode:# def __init__(self, x):# self.val = x# self.left = None# self.right = Noneclass 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 = Noneclass Solution:def postorderTraversal(self, root: TreeNode) -> List[int]:if not root:return []stack = [root]result = []p = rootwhile 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
