vlambda博客
学习文章列表

二叉树的递归与迭代遍历

本文将针对二叉树中几种常见的遍历方法进行介绍。

遍历方式

前序遍历

前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。

中序遍历

中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。

后序遍历

后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。

递归实现

递归实现二叉树的遍历是非常简单的,其核心就是 深度优先搜索(DFS) 算法。

由于比较简单,三种遍历方式的实现代码只是 深度优先搜索 过程中执行顺序的区别,故模版如下:

 
   
   
 
  1. public class Solution {


  2. // 递归遍历二叉树

  3. public List<Integer> preorderTraversal(TreeNode root) {

  4. ArrayList<Integer> list = new ArrayList<>();

  5. if (root == null) return list;


  6. dfs(root, list);

  7. return list;

  8. }


  9. private void dfs(TreeNode root, ArrayList<Integer> list) {

  10. if (root == null) return;


  11. // 前序遍历 根 -> 左 -> 右

  12. list.add(root.val); // 根

  13. dfs(root.left, list); // 左

  14. dfs(root.right, list); // 右


  15. // 中序遍历 右 -> 根 -> 右

  16. // dfs(root.left, list);

  17. // list.add(root.val);

  18. // dfs(root.right, list);


  19. // 后序遍历 左 -> 右 -> 根

  20. // dfs(node.left, list);

  21. // dfs(node.right, list);

  22. // list.add(node.val);

  23. }

  24. }

迭代实现

前序遍历

通过迭代对前序遍历需要一个栈进行辅助,其负责对不同层级父子节点进行迭代存储。

 
   
   
 
  1. class Solution {

  2. public List<Integer> preorderTraversal(TreeNode root) {

  3. ArrayList<Integer> list = new ArrayList<>();

  4. Stack<TreeNode> stack = new Stack<>();


  5. TreeNode curr = root;


  6. // 1.退出最外层迭代的条件是,指针指向null,且栈为空

  7. while (curr != null || !stack.isEmpty()) {

  8. // 2.内层循环按顺序入栈,同时更新当前指针

  9. // 4.这时候也可能是开始遍历右节点

  10. while (curr != null) {

  11. stack.push(curr);

  12. list.add(curr.val);

  13. curr = curr.left;

  14. }

  15. // 3.返回父节点,并将指针指向右节点

  16. curr = stack.pop();

  17. curr = curr.right;

  18. }


  19. return list;

  20. }

  21. }

中序遍历

中序遍历和前序遍历思想是一致的,区别仅仅在于根节点在左叶子节点添加之后添加:

 
   
   
 
  1. class Solution {

  2. public List<Integer> preorderTraversal(TreeNode root) {

  3. List<Integer> list = new ArrayList<>();

  4. Stack<TreeNode> stack = new Stack<>();


  5. TreeNode curr = root;


  6. // 1.退出最外层迭代的条件是,指针指向null,且栈为空

  7. while (!stack.isEmpty() || curr != null) {

  8. // 2.内层循环按顺序入栈,同时更新当前指针

  9. // 5.这时候也可能是开始遍历右节点

  10. while (curr != null) {

  11. stack.push(curr.left);

  12. curr = curr.left;

  13. }

  14. // 3.返回父节点,并加入数组中

  15. curr = stack.pop();

  16. list.add(curr.val);

  17. // 4.将指针指向右节点

  18. curr = curr.right;

  19. }

  20. return list;

  21. }

  22. }

后序遍历

后序遍历 LeetCode 官方题解给出了一个额外的思路,对于树的 后序遍历 而言,其遍历顺序与 广度优先搜索(BFS) 恰恰是相反的:

如图所示, BFS的遍历顺序是 1->2->3->4->5,而相同的树后序遍历顺序则是 4->5->2->3->1

因此,后序遍历的思路如下:

从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。

因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。

 
   
   
 
  1. /**

  2. * Definition for a binary tree node.

  3. * public class TreeNode {

  4. * int val;

  5. * TreeNode left;

  6. * TreeNode right;

  7. * TreeNode(int x) { val = x; }

  8. * }

  9. */

  10. class Solution {

  11. public List<Integer> postorderTraversal(TreeNode root) {

  12. LinkedList<Integer> output = new LinkedList<>();

  13. // 和传统的bfs不同,这里并没有用 Queue

  14. // 因为顺序是相反的,这里并不是取第一个元素,而是取栈顶的元素(即同层级节点从右->左遍历)

  15. Stack<TreeNode> stack = new Stack<>();


  16. if (root == null) return output;


  17. stack.push(root);


  18. while(!stack.isEmpty()) {

  19. TreeNode node = stack.pop();

  20. output.addFirst(node.val);


  21. if (node.left != null) {

  22. stack.push(node.left);

  23. }

  24. if (node.right != null) {

  25. stack.push(node.right);

  26. }

  27. }

  28. return output;

  29. }

  30. }

参考 & 感谢

文章绝大部分内容节选自 LeetCode,概述:

  • https://leetcode-cn.com/explore/learn/card/data-structure-binary-tree/2/traverse-a-tree/7/

例题:

  • https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

  • https://leetcode-cn.com/problems/binary-tree-inorder-traversal/

  • https://leetcode-cn.com/problems/binary-tree-postorder-traversal