「数据结构与算法」树的遍历

class TreeNode {
    int val;
    // 左子树
    TreeNode left;
    // 右子树
    TreeNode right;
    // 构造方法
    TreeNode(int x) {
        val = x;
    }
}

先序遍历

递归先序遍历:

// 递归先序遍历
void recursionPreOrderTraversal(TreeNode root) {
    if (root != null) {
        // 先序
        System.out.print(root.val + " ");
        recursionPreOrderTraversal(root.left);
        recursionPreOrderTraversal(root.right);
    }
}

在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用来进行暂存

非递归先序遍历:

// 非递归先序遍历
void preOrderTraversal(TreeNode root) {
    // 该栈用于暂存节点
    Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
    // 游标
    TreeNode node = root;
    // 最后一个节点时,左右子树为空,并且栈也为空
    while (node != null || !treeNodeStack.isEmpty()) {
        while (node != null) {
            // 先序访问
            System.out.print(node.val + " ");
            treeNodeStack.push(node);
            node = node.left;
        }
        // 此时左子树为空,考虑右子树
        // 如果栈已空,无需考虑
        if (!treeNodeStack.isEmpty()) {
            // 弹出栈顶元素
            node = treeNodeStack.pop();
            node = node.right;
        }
    }
}

中序遍历

递归中序遍历:

// 递归中序遍历
void recursionInOrderTraversal(TreeNode root) {
    if (root != null) {
        recursionInOrderTraversal(root.left);
        // 中序
        System.out.print(root.val + " ");
        recursionInOrderTraversal(root.right);
    }
}

在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用来进行暂存

非递归中序遍历:

// 非递归中序遍历
void inOrderTraversal(TreeNode root) {
    // 该栈用于暂存节点
    Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
    // 游标
    TreeNode node = root;
    // 最后一个节点时,左右子树为空,并且栈也为空
    while (node != null || !treeNodeStack.isEmpty()) {
        while (node != null) {
            treeNodeStack.push(node);
            node = node.left;
        }
        // 此时左子树为空,考虑右子树
        // 如果栈已空,无需考虑
        if (!treeNodeStack.isEmpty()) {
            // 弹出栈顶元素
            node = treeNodeStack.pop();
            // 中序访问
            System.out.print(node.val + " ");
            node = node.right;
        }
    }
}

后序遍历

递归后序遍历

// 递归后序遍历
void recursionPostOrderTraversal(TreeNode root) {
    if (root != null) {
        recursionPostOrderTraversal(root.left);
        recursionPostOrderTraversal(root.right);
        // 后序
        System.out.print(root.val + " ");
    }
}

非递归后序遍历:

// 非递归后序遍历
void postOrderTraversal(TreeNode root) {
    // 该栈用于暂存节点
    Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
    // 游标
    TreeNode node = root;
    // 保存最后访问节点
    TreeNode lastVisit = root;
    // 最后一个节点时,左右子树为空,并且栈也为空
    while (node != null || !treeNodeStack.isEmpty()) {
        while (node != null) {
            treeNodeStack.push(node);
            node = node.left;
        }
        // 查看当前栈顶元素
        node = treeNodeStack.peek();
        // 如果其右子树也为空,或者右子树已经访问
        // 则可以直接输出当前节点的值
        if (node.right == null || node.right == lastVisit) {
            // 后序访问
            System.out.print(node.val + " ");
            treeNodeStack.pop();
            lastVisit = node;
            node = null;
        } else {
            //否则,继续遍历右子树
            node = node.right;
        }
    }
}

前序遍历之所以最简单,是因为遍历过程中最先遇到的根节点是最先访问的,而在后序遍历中,最先遇到的根节点是最后访问的。后序遍历的顺序是“左-右-根”,如果能倒序一下成为“根-右-左”,那么遍历的整体难度就会下降。同时,考虑到栈本身是天然的倒序工具,所以我们可以考虑用一个栈将输出顺序反过来即可。这就引出了双栈法,简单来说其思路是:

  1. 用一个栈实现“根-右-左”的遍历顺序
  2. 用一个将“根-右-左”倒序成“左-右-根”

双栈法后序遍历:

// 双栈法后序遍历
void preOrderTraversal(TreeNode root) {
    // 该栈用于暂存节点
    Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
    // 该栈用于倒序
    Stack<TreeNode> reverseStack = new Stack<>();
    // 游标
    TreeNode node = root;
    // 最后一个节点时,左右子树为空,并且栈也为空
    while (node != null || !treeNodeStack.isEmpty()) {
        while (node != null) {
            // 先序访问
            reverseStack.push(node);
            treeNodeStack.push(node);
            node = node.right;
        }
        // 此时左子树为空,考虑右子树
        // 如果栈已空,无需考虑
        if (!treeNodeStack.isEmpty()) {
            // 弹出栈顶元素
            node = treeNodeStack.pop();
            node = node.left;
        }
    }
    while (!reverseStack.isEmpty()) {
        System.out.print(reverseStack.pop().val + " ");
    }
}

参考

https://www.jianshu.com/p/456...
https://zhuanlan.zhihu.com/p/...
https://segmentfault.com/a/11...


山庄的铁匠
15 声望11 粉丝