zoukankan      html  css  js  c++  java
  • 树的前中后顺遍历(递归、栈、颜色标记)---Java实现

    二叉树的前中后顺遍历(递归、栈)Java实现

    前序遍历

    力扣144

    • 递归
    import java.util.ArrayList;
    import java.util.List;
    
    public class PreOrder {
    
        public List<Integer> preOrder(TreeNode root) {
            List<Integer> res = new ArrayList<>();
    
            helper(root, res);
    
            return res;
        }
    
        public void helper(TreeNode root, List<Integer> res) {
            if (root != null) {
                res.add(root.val);
    
                if (root.left != null) {
                    helper(root.left, res);
                }
    
                if (root.right != null) {
                    helper(root.right, res);
                }
    
            }
        }
    }
    
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x;}
    }
    
    public List<Integer> stackTravers_preorder(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
    
            TreeNode curr = root;
            while (curr != null || !stack.isEmpty()) {
                while (curr != null) {
                    res.add(curr.val);
                    stack.push(curr);
                    curr = curr.left;
                }
    
                if (!stack.isEmpty()) {
                    curr = stack.pop();
                    curr = curr.right;
    
                }
            }
    
            return res;
        }
    

    中序遍历

    力扣94

    • 递归
    import java.util.ArrayList;
    import java.util.List;
    
    public class PreOrder {
    
        public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            helper(root, res);
            return res;
        }
    
        // recursive
        public void helper(TreeNode root, List<Integer> res) {
            if (root != null) {
                if (root.left != null) {
                    helper(root.left, res);
                }
    
                res.add(root.val);
    
                if (root.right != null) {
                    helper(root.right, res);
                }
    
            }
        }
    }
    
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x;}
    }
    
    public List<Integer> stackTraversl(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
    
            TreeNode curr = root;
            while (curr != null || !stack.isEmpty()) {
    
                // push the left tree into stack
                while (curr != null) {
                    stack.push(curr);
                    curr = curr.left;
                }
    
        		// when reached leaf node
                curr = stack.pop();
                res.add(curr.val);
                curr = curr.right;
            }
    
            return res;
        }
    

    后续遍历

    力扣145

    • 递归
    import java.util.ArrayList;
    import java.util.List;
    
    public class PostOrder {
    
        public List<Integer> postOrderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            helper(root, res);
            return res;
        }
    
        // recursive
        public void helper(TreeNode root, List<Integer> res) {
            if (root != null) {
                if (root.left != null) {
                    helper(root.left, res);
                }
                if (root.right != null) {
                    helper(root.right, res);
                }
                res.add(root.val);
            }
        }
    }
    
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x;}
    }
    

    算法核心思想:
    首先要搞清楚先序、中序、后序的非递归算法共同之处:用栈来保存先前走过的路径,以便可以在访问完子树后,可以利用栈中的信息,回退到当前节点的双亲节点,进行下一步操作。
    后序遍历的非递归算法是三种顺序中最复杂的,原因在于,后序遍历是先访问左、右子树,再访问根节点,而在非递归算法中,利用栈回退到时,并不知道是从左子树回退到根节点,还是从右子树回退到根节点,如果从左子树回退到根节点,此时就应该去访问右子树,而如果从右子树回退到根节点,此时就应该访问根节点。所以相比前序和后序,必须得在压栈时添加信息,以便在退栈时可以知道是从左子树返回,还是从右子树返回进而决定下一步的操作。
    ————————————————
    版权声明:本文为CSDN博主「coder__666」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/coder__666/article/details/80349039

    public List<Integer> stackTravers_postorder(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            Stack<Integer> stack2 = new Stack<>();      // 辅助栈,用于保存,压入栈的是左子树还是右子树
    
            TreeNode curr = root;
            int left = 1;  // note left tree
            int right = 2; // note right tree
    
            while (curr != null || !stack.isEmpty()) {
    
                while (curr != null) {
                    stack.push(curr);
                    stack2.push(left);
                    curr = curr.left;
                }
    
                while (!stack.isEmpty() && stack2.peek() == right) {
                    // 如果是从左子节点返回父节点,则任务完成,将两个栈的栈顶弹出,记录结果
                    stack2.pop();
                    res.add(stack.pop().val);
                }
    
                if (!stack.isEmpty() && stack2.peek() == left) {
                    // 如果是从左子树返回,则先将辅助栈弹出栈顶,下一个应该压栈的是右子树
                    //
                    stack2.pop();
                    stack2.push(right);
                    curr = stack.peek().right;
    
                }
    
            }
    
    
            return res;
        }
    

    颜色标记法(通用)

    颜色标记法

    方题解中介绍了三种方法来完成树的中序遍历,包括:

    递归
    借助栈的迭代方法
    莫里斯遍历
    在树的深度优先遍历中(包括前序、中序、后序遍历),递归方法最为直观易懂,但考虑到效率,我们通常不推荐使用递归。

    栈迭代方法虽然提高了效率,但其嵌套循环却非常烧脑,不易理解,容易造成“一看就懂,一写就废”的窘况。而且对于不同的遍历顺序(前序、中序、后序),循环结构差异很大,更增加了记忆负担。

    因此,我在这里介绍一种“颜色标记法”(瞎起的名字……),兼具栈迭代方法的高效,又像递归方法一样简洁易懂,更重要的是,这种方法对于前序、中序、后序遍历,能够写出完全一致的代码。

    其核心思想如下:

    使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
    如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
    如果遇到的节点为灰色,则将节点的值输出。

    public class ColorMethod {
    
        // 定义含有颜色信息的树节点
        class ColorNode{
            TreeNode node;
            int color;
    
            public ColorNode(TreeNode node, int color) {
                this.node = node;
                this.color = color;
            }
        }
        
        class TreeNode {
            int val;
            TreeNode left;
            TreeNode right;
            TreeNode(int x) { val = x;}
    	}
    
        private final int white = 0;
        private final int gray = 1;
    
    
        public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
    
            if (root == null) {
                return res;
            }
    
            Stack<ColorNode> stack = new Stack<>();
            stack.push(new ColorNode(root, white));
    
            while (!stack.isEmpty()) {
                ColorNode curr = stack.pop();
    
                // 如果此节点没有访问过
                if (curr.color == white) {
                    if (curr.node.right != null) {
                        stack.push(new ColorNode(curr.node.right, white));
                    }
                    // 置为灰色
                    stack.push(new ColorNode(curr.node, gray));
                    if (curr.node.left != null) {
                        stack.push(new ColorNode(curr.node.left, white));
                    }
                } else {
                    res.add(curr.node.val);
                }
    
            }
            return res;
        }
    
    
    }
    

    N叉树的前序遍历

    力扣 589

    递归

    /**
     * N叉树的前序遍历
     */
    public class Recursive {
    
        // 使用递归来前序遍历
        public List<Integer> preorder(Node root) {
            List<Integer> res = new ArrayList<>();
            if (root == null) {
                return res;
            }
            helper(root, res);
            return res;
        }
    
        private void helper(Node curr, List<Integer> res) {
            if (curr != null) {
                res.add(curr.val);
                for (Node temp : curr.children) {
                    helper(temp, res);
                }
            }
        }
    
    }
    
    class Node {
        public int val;
        public List<Node> children;
    
        public Node() {}
    
        public Node(int _val) {
            val = _val;
        }
    
        public Node(int _val, List<Node> _children) {
            val = _val;
            children = _children;
        }
    }
    

    栈-迭代

    /**
     * 使用栈,采用迭代的方式,来实现多叉树的遍历
     */
    public class StackIterator {
    
        public List<Integer> preorder(Node root) {
    //        List<Node> stack = new LinkedList<>();
            Stack<Node> stack = new Stack<>();
            List<Integer> res = new LinkedList<>();
    
            if (root == null) {
                return res;
            }
    
            stack.add(root);
    
            while (!stack.isEmpty()) {
                // 前序遍历,因此是root left tight
                // 入栈的顺序要反过来
                Node curr = stack.pop();
                res.add(curr.val);
                if (curr.children != null) {
                    Collections.reverse(curr.children);
                    for (Node temp : curr.children) {
                        stack.push(temp);
                    }
                }
            }
    
            return res;
        }
    

    颜色标记法

    public class ColorMethod {
    
        private final int white = 0;
        private final int gray = 1;
    
        public List<Integer> preorder(Node root) {
            List<Integer> res = new LinkedList<>();
    
            if (root == null) {
                return res;
            }
    
            Stack<ColorNode> stack = new Stack<>();
            stack.push(new ColorNode(root, white));
    
            while (!stack.isEmpty()) {
                ColorNode curr = stack.pop();
    
                if (curr.color == white) {
                    // 以相反的顺序压栈
                    if (curr.node.children != null) {
                        Collections.reverse(curr.node.children);
                        for (Node temp : curr.node.children) {
                            stack.push(new ColorNode(temp, white));
                        }
                        stack.push(new ColorNode(curr.node, gray));
                    }
                } else {
                    res.add(curr.node.val);
                }
            }
    
            return res;
        }
    
    
    }
    
    
    class Node {
        public int val;
        public List<Node> children;
    
        public Node() {}
    
        public Node(int _val) {
            val = _val;
        }
    
        public Node(int _val, List<Node> _children) {
            val = _val;
            children = _children;
        }
    }
    
    // 定义含有颜色信息的树节点
    class ColorNode{
        Node node;
        int color;
    
        public ColorNode(Node node, int color) {
            this.node = node;
            this.color = color;
        }
    }
    

    参考链接

    CSDN

    力扣 中序遍历 题解

    力扣题解-颜色标记法

  • 相关阅读:
    javascript 拷贝详解
    javascript 递归函数详解
    移动端布局解决方案
    Flexbox
    CSS中越界问题的经典解决方案
    移动应用测试方法与思路
    不是人家太装逼,而是我们太low
    GUI自动化测试策略
    GUI测试稳定性的关键技术
    GUI测试还能这么玩(Page Code Gen + Data Gen + Headless)
  • 原文地址:https://www.cnblogs.com/tangg/p/13196890.html
Copyright © 2011-2022 走看看