二叉树遍历,在面试中出现的概率极大,常常需要面试者掌握二叉树的三种遍历方式,前序遍历、中序遍历、后序遍历,同时又要要求面试者掌握递归和非递归实现。

前序遍历、中序遍历、后序遍历三个遍历中,递归的实现方式最简单,而且有规律可循。
三种变量的非递归实现较难。

三个遍历方式的定义分别是:

  • 前序遍历先访问根节点,再前序遍历左子树,再前序遍历右子树
  • 中序遍历:先中序遍历左子树,再访问根节点,再中序遍历右子树
  • 后序遍历:先后序遍历左子树,再后序遍历右子树,再访问根节点


Tip:以根节点的访问顺序作为前中后遍历的定义,且三种遍历方式都是先遍历左子树,再遍历右子树。**

本节将会教你用一套模板实现三种递归方式,面试不再愁!!!

前序遍历

递归

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.left = None
  6. # self.right = None
  7. class Solution:
  8. def preorderTraversal(self, root: TreeNode) -> List[int]:
  9. if not root:
  10. return []
  11. return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

非递归

  • 模板法

先记住先序遍历,中序和后序遍历只需要在此基础上修改即可

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.left = None
  6. # self.right = None
  7. class Solution:
  8. def preorderTraversal(self, root: TreeNode) -> List[int]:
  9. if root is None:
  10. return []
  11. result = [] # 保存结果
  12. stack = [root] # 压入 root 节点
  13. while stack:
  14. p = stack.pop() # 访问过的节点弹出
  15. if p is None: # 空节点表示之前已经访问过了,现在需要处理除了递归之外的内容
  16. p = stack.pop() # 彻底从栈中移除
  17. result.append(p.val)
  18. else:
  19. if p.right: # 右节点先压栈,最后处理
  20. stack.append(p.right)
  21. if p.left:
  22. stack.append(p.left)
  23. stack.append(p) # 当前节点重新压栈(留着以后处理),因为先序遍历所以最后压栈
  24. stack.append(None) # 在当前节点之前加入一个空节点表示已经访问过了
  25. return result
  • 非模板

前序遍历的非递归实现比较简单
建立一个栈 stack,先把 root 节点压入,然后 while 判断 stack 是否为空作为循环退出的条件
进入 while 循环以后,弹出栈顶的节点,若该节点有右子节点、左子节点的话,压入栈中(注意:压入栈的时候先压入右子节点,再压入左子节点,这是因为栈是后入先出,左子节点最后被压入,但是最先被弹出)

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.left = None
  6. # self.right = None
  7. class Solution:
  8. def preorderTraversal(self, root: TreeNode) -> List[int]:
  9. if not root:
  10. return []
  11. stack = [root]
  12. result = []
  13. while stack:
  14. node = stack.pop()
  15. if node:
  16. result.append(node.val)
  17. if node.right:
  18. stack.append(node.right) # 先压入右边
  19. if node.left:
  20. stack.append(node.left) # 后压入左边
  21. return result

中序遍历

递归

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.left = None
  6. # self.right = None
  7. class Solution:
  8. def inorderTraversal(self, root: TreeNode) -> List[int]:
  9. if not root:
  10. return []
  11. 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

  1. - 非模板
  2. > 创建一个栈 stack,一个 cur 变量,用来记录当前的节点,把 cur stack 非空作为循环的条件,进入循环,判断 cur 节点是否为空,非空的话,往复变量 当前节点的左子节点,压入栈中。
  3. ```python
  4. # Definition for a binary tree node.
  5. # class TreeNode:
  6. # def __init__(self, x):
  7. # self.val = x
  8. # self.left = None
  9. # self.right = None
  10. class Solution:
  11. def inorderTraversal(self, root: TreeNode) -> List[int]:
  12. if not root:
  13. return []
  14. stack = [] # 最开始不压入 root 节点
  15. result = []
  16. cur = root #
  17. while cur or stack: # 或
  18. while cur: # 不断把当前节点的 左子节点压入
  19. stack.append(cur)
  20. cur = cur.left
  21. cur = stack.pop() # 弹出
  22. result.append(cur.val)
  23. cur = cur.right
  24. return result

后序遍历

递归

  1. # Definition for a binary tree node.
  2. # class TreeNode:
  3. # def __init__(self, x):
  4. # self.val = x
  5. # self.left = None
  6. # self.right = None
  7. class Solution:
  8. def postorderTraversal(self, root: TreeNode) -> List[int]:
  9. if not root:
  10. return []
  11. 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

  1. - 非模板<br />
  2. > 后序遍历弹出(输出值)的条件是**当前节点是叶节点或者子节点也访问过了**
  3. ```python
  4. # Definition for a binary tree node.
  5. # class TreeNode:
  6. # def __init__(self, x):
  7. # self.val = x
  8. # self.left = None
  9. # self.right = None
  10. class Solution:
  11. def postorderTraversal(self, root: TreeNode) -> List[int]:
  12. if not root:
  13. return []
  14. stack = [root]
  15. result = []
  16. p = root
  17. while stack:
  18. top = stack[-1]
  19. # 如果是叶子结点,儿子也已经有访问过,则输出它,并记录下当前输出结点
  20. if (top.left == p) or (top.right == p) or \
  21. (not top.left and not top.right): # 叶子元素
  22. p = stack.pop()
  23. result.append(p.val)
  24. # 如果它不是叶子结点,儿子也没有访问,那么就需要将它的右儿子,左儿子压入。
  25. else:
  26. # 先访问的后加入
  27. if top.right: # 先加右
  28. stack.append(top.right)
  29. if top.left: # 再加左
  30. stack.append(top.left)
  31. 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,标记已经被访问

参考