算法数据结构面试——树的前序,中序,后序,层级遍历(非递归)

二叉树的遍历是数据结构中的一个经典问题,我们常用两种方式来进行遍历:递归,非递归。递归方式很容易理解和实现,但是在数据量大的情况下,递归层级太深,会导致虚拟机栈的栈溢出,系统稳定性会很差。所以在生产一般不会使用递归来实现。接下来,我们讲一下这四种遍历的非递归写法。

首先,我们定义一个二叉树:

前序遍历

前序遍历,也叫做先序遍历。它的遍历规则是首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。

根->左->右

这里,我们遍历的时候用到了另一个数据结构:栈。利用它先进后出的特点来实现遍历。下面我们通过画图来展示一下整个遍历过程。

  1. 首先是创建一个栈,根节点入栈。
  1. A出栈,并且存入结果中。获取A的左右节点BC,先右后左入栈。(保证左节点B先出栈)
  1. B出栈,并且存入结果中。获取B的左右节点DE,先右后左入栈。(保证左节点D先出栈)
  1. D出栈,并且存入结果中。获取D的左右节点FG,先右后左入栈。(保证左节点F先出栈)
  1. F出栈,并存入结果中。由于F没有左右节点,所以进行下次循环。
  1. G出栈,并存入结果中。由于G没有左右节点,所以进行下次循环。
  2. 以此类推,依次出栈E,C。直到栈为空。最终我们输出了结果ABDFGEC。

代码如下:

    public static class TreeNode {
        String val;
        TreeNode left;
        TreeNode right;
        TreeNode(String x) { val = x; }
    }
    // 非递归(栈)先序遍历树
    public static List<String> preorderTraversal(TreeNode root) {
        List<String> res = new ArrayList<>();
        if (root == null) return res;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            res.add(node.val);
            if (node.right != null) stack.push(node.right);
            if (node.left != null) stack.push(node.left);
        }
        return res;
    }

中序遍历

中序遍历,它的遍历规则是首先遍历左子树然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,然后访问根结点,最后遍历右子树。

左->根->右

下面我们通过画图来展示一下整个遍历过程。

  1. 首先创建一个栈,并且定义一个指针,指向根节点。
  1. 先通过指针访问左子树直到最低端,并且将元素压入栈内。
  1. F出栈,并存入结果中。将结果T指向F的右节点。右节点为空,进入下次循环。
  2. D出栈,并存入结果中。将结果T指向D的右节点G。
  3. 指针访问G的左子树直到最低端,并且将元素压入栈内。
  1. G出栈,并存入结果中。将结果T指向G的右节点。右节点为空,进入下次循环。
  2. B出栈,并存入结果中。将结果T指向B的右节点E。
  3. 指针访问E的左子树直到最低端,并且将元素压入栈内。
  1. E出栈,并存入结果中。将结果T指向E的右节点。右节点为空,进入下次循环。
  2. A出栈,并存入结果中。将结果T指向A的右节点C。
  3. 指针访问C的左子树直到最低端,并且将元素压入栈内。
  1. C出栈,并存入结果中。将结果T指向C的右节点。循环结束。输出:FDGBEAC。

代码如下:

    // 非递归(栈)中序遍历树
    public static List<String> inorderTraversal(TreeNode root) {
        List<String> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root;

        while (current != null || !stack.isEmpty()) {
            // 访问左子树直到最左端
            while (current != null) {
                stack.push(current);
                current = current.left;
            }
            // 访问栈顶元素(即最左端节点)
            current = stack.pop();
            result.add(current.val); // 访问节点值
            // 转向右子树
            current = current.right;
        }
        return result;
    }

后序遍历

后序遍历,它的遍历规则是首先遍历左子树,然后遍历右子树,最后访问根结点。在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后访问根结点。

左->右->根

下面我们通过画图来展示一下整个遍历过程。

  1. 首先是创建一个栈,根节点入栈。
  1. A出栈,并且存入结果中。获取A的左右节点BC,先左后右入栈。(保证右节点C先出栈)

这里我们发现,逻辑跟前序遍历差不多,只不过先右后左改成了先左后右。后面我就省略跳过了。最终,我们得到了一个结果。

看出什么了,是不是结果反了。所以,我们需要把结果倒序。我们可以再遍历一下,将结果倒序。也可以将结果存入栈中,利用先进后出的特点,将结果倒序。最终结果成FGDEBCA。

代码如下:

// 非递归(栈)后序遍历树
    public static List<String> postorderTraversal(TreeNode root) {
        List<String> result = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        Stack<String> outputStack = new Stack<>(); // 用于存储后序遍历的结果
        stack.push(root);

        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            outputStack.push(node.val); // 先将节点值放入输出栈中,这样可以保证后序访问的顺序
            if (node.left != null) {
                stack.push(node.left); // 先左后右,这样可以保证左子节点先于右子节点被处理
            }
            if (node.right != null) {
                stack.push(node.right);
            }
        }

        // 输出后序遍历的结果,注意这里是反转输出栈以得到正确的后序遍历顺序
        while (!outputStack.isEmpty()) {
            result.add(outputStack.pop());
        }
        return result;
    }

层级遍历

层级遍历,它的遍历规则是首先访问根结点,然后访问第二级,然后访问第三级,直到结束。

这里,我们遍历的时候用到了另一个数据结构:队列。利用它先进先出的特点来实现遍历。下面我们通过画图来展示一下整个遍历过程。

  1. 首先是创建一个队列,根节点入队。
  1. A出队,并且存入结果中。获取A的左右节点BC,先左后右入队。(保证左节点B先出队)

后面的逻辑跟前序遍历差不多,后面我就省略跳过了。最终,我们得到了一个结果ABCDEFG。

代码如下:

    // 非递归(队列)层级遍历树
    public static List<String> levelOrderTraversal(TreeNode root) {
        List<String> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            res.add(node.val);
            if (node.left != null) queue.add(node.left);
            if (node.right != null) queue.add(node.right);
        }
        return res;
    }

至此,我们实现了二叉树的前序遍历,中序遍历,后序遍历,层级遍历。掌握这些数据结构,对我们编程思维有很大的帮助。

原文链接:,转发请注明来源!