今天分享下二叉树的前中序遍历的迭代实现。

我们都知道用递归来实现前中后序遍历是很简单的,非递归的实现则麻烦一些,我们先看看非递归的思路是什么。

定义TreeNode结构如下:

public class TreeNode {

  public int val;
  public TreeNode left;
  public TreeNode right;

  public TreeNode(int x) {
    val = x;
  }
}

前序遍历

思路

前序遍历的顺序是根节点-左节点-右节点。之后我们需要一个栈,首先把根节点压入栈。

之后我们就开始循环弹出栈元素,然后打印该元素的值,之后判断如果该元素的右节点不为空,则将右节点压入栈,之后在判断该元素的左节点是否为空,如果不为空再将左节点压入栈。然后重复循环。

代码

public static void preorderTraversal(TreeNode root) {
    Stack stack = new Stack<>();
    //根节点入栈
    stack.push(root);
    while (!stack.isEmpty()) {
      //弹出元素并打印
      TreeNode node = stack.pop();
      System.out.println(node.val);
      //判断右节点是否为空
      if (node.right != null) {
        stack.push(node.right);
      }
      //判断左节点是否为空
      if (node.left != null) {
        stack.push(node.left);
      }
    }
  }

中序遍历

思路

中序遍历则比前序麻烦一些,中序的顺序是左节点-根节点-右节点。

步骤一:我们将根节点和根节点的左节点压入栈,再去对根节点的左节点做同样的操作,直到把叶子结点压入栈。

步骤二:之后弹出元素并打印记录,之后判断如果该元素有右节点,则将右节点视为根节点,重复步骤1。

代码

public List inorderTraversal(TreeNode root) {
    List r = new ArrayList<>();
    Stack stack = new Stack<>();
    //根节点和根节点的左节点入栈
    TreeNode node = root;
    while (node != null) {
      stack.push(node);
      node = node.left;
    }

    while (!stack.isEmpty()) {
      //弹出元素
      TreeNode popNode = stack.pop();
      //记录元素值
      r.add(popNode.val);
      if (popNode.right != null) {
        //如果元素的右节点不为空则视为根节点重复步骤一
        TreeNode tmp = popNode.right;
        while (tmp != null) {
          stack.push(tmp);
          tmp = tmp.left;
        }
      }
    }
    return r;
  }

总结

对于二叉树的前中序遍历的非递归实现,我们都使用到了栈。递归的复杂度是O(logn),而迭代的复杂度是O(n)。但是我们在解决某些问题的时候,使用迭代比递归好,比如写一个方法,调用一次返回一个以前序遍历得到的元素,因为迭代可以记录迭代的位置,而递归则不好记录栈帧的状态。