二叉树遍历--非递归

120 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

二叉树的递归遍历比较简单,但是在遍历节点较多的二叉树时有可能会栈溢出,所以还是非递归更安全

递归的本质还是栈,用栈模仿递归的过程即可

前序遍历

对于每个节点来说,先遍历节点值,再遍历左子树,最后遍历右子树

  1. 首先将当前遍历位置设为根节点
  2. 在当前遍历位置不为空或栈不为空时(表示递归还未结束)进行循环
  3. 每次循环时,将当前节点加入遍历结果并且放入栈,然后将当前节点指向当前节点左儿子,重复这个过程直至当前节点为空(表示没有左儿子了);此时栈顶元素即为还未遍历的最左节点,将其出栈,并将当前节点指向出栈节点右儿子(类似递归遍历左子树结束后,进入右子树递归)

具体看代码

public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode node = root;
        while (!stack.isEmpty() || node != null) {
            if (node != null) {
                // 优先遍历节点值
                ans.add(node.val);
                // 当前节点入栈,类似存下递归的上层
                stack.push(node);
                // 遍历左子树
                node = node.left;
            }
            // 左子树为空,栈顶元素出栈,类似返回上层递归
            node = stack.pop();
            // 遍历右子树
            node = node.right;
        }
        return ans;
    }

中序遍历

遍历顺序为 左子树 -> 本节点值 -> 右子树

public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode node = root;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                // 当前节点入栈,类似存下递归的上层
                stack.push(node);
                // 优先遍历左子树
                node = node.left;
            }
            // 左子树为空时,栈顶元素出栈,返回上层递归
            node = stack.pop();
            // 遍历上层递归节点值
            ans.add(node.val);
            // 最后遍历右子树
            node = node.right;
        }
        return ans;
    }

后序遍历

遍历顺序为 左子树 -> 右子树 -> 本节点值

需要注意,后序和前两个不同,需要额外处理右子树遍历的问题,具体看代码

public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode node = root;
        TreeNode prev = null;   // 最近一次访问的节点
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                // 当前节点入栈,类似存下递归的上层
                stack.push(node);
                // 优先遍历左子树
                node = node.left;
            }
            // 栈顶元素出栈,返回上层递归
            node = stack.pop();
            // 如果右子树不为空且不是上次遍历过的位置
            if (node.right != null && node.right != prev) {
                // 放入当前节点,类似存下递归的上层
                stack.push(node);
                // 遍历右子树
                node = node.right;
            } else {
                // 存下最近一次遍历的节点
                prev = node;
                // 遍历当前节点值
                ans.add(node.val);
                // 置空,防止重复进入左子树
                node = null;
            }
        }
        return ans;
    }