• 树的遍历(一)


    树的递归遍历框架


    二叉树遍历框架,典型的非线性递归遍历结构:

    /*
          基本的二叉树结点
    */
    class TreeNode{
          int val;
          TreeNode left, right;
    }
    
    void traverse(TreeNode root){
          traverse(root.left);
          traverse(root.right);
    }
    

    二叉树框架可以扩展为N叉树的遍历框架:

    /* 基本的 N 叉树节点 */
    class TreeNode {
        int val;
        TreeNode[] children;
    }
    
    void traverse(TreeNode root) {
        for (TreeNode child : root.children)
            traverse(child);
    }
    

    二叉树的遍历套用上面的框架:

    void traverse(TreeNode root) {
        // 前序遍历
        traverse(root.left)
        // 中序遍历
        traverse(root.right)
        // 后序遍历
    }
    

    同理,N叉树遍历的递归方法如下:

    void traverse(TreeNode root) {
        // 前序遍历
        for (TreeNode child : root.children){
              traverse(child);
        // 后序遍历
    }
    
    参考资料:labuladong的算法小抄

    树的遍历递归框架如上,下面重点记录非递归遍历树的方法。

    二叉树


    前序遍历

    实现二叉树的非递归前序遍历,我们可以利用这一数据结构,保证栈顶节点就是我们当前的遍历节点。

    1. node = root
    2. 如果 node != null || !stack.isEmpty()
        1)如果node != null
          a. 将值add到res中
          b. 将node推入栈中
          c. node等于node.left,遍历左子树
        2)弹出栈顶,node = node.right开始遍历右子树
    3. 返回结果
    public List<Integer> preorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<Integer>();
            Deque<TreeNode> stack = new LinkedList<TreeNode>();
            TreeNode node = root;
            while (node != null || !stack.isEmpty()){
                while (node != null ){
                    res.add(node.val);
                    stack.push(node);
                    node = node.left;
                }
                node = stack.pop();
                node = node.right;
            }
            return res;
        }
    

    中序遍历

    也是利用栈来实现二叉树的非递归中序遍历。代码基本相同,唯一不同的是node值在入栈后,再在出栈时add到res中。

    public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Deque<TreeNode> stack = new LinkedList<>();
            TreeNode node = root;
            while (node != null || !stack.isEmpty()){
                while(node != null){
                    stack.push(node);
                    node = node.left;
                }    
                node = stack.pop();
                res.add(node.val);   // 出栈add
                node = node.right;
            }
            return res;
        }
    

    后序遍历

    同样也是利用栈来完成,该解法的核心是同一个节点进两次栈,第一次出栈是进入该节点的右子树,第二次出栈将值add到res中。

    public List<Integer> postorderTraversal(TreeNode root) {
            List<Integer> res = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            while (true){
                while (root != null){  // 先将root的左子树都压入栈中
                    stack.push(root);
                    stack.push(root);
                    root = root.left;
                }
                if (stack.isEmpty())
                    break;
                root = stack.pop();
                if (!stack.isEmpty() && root == stack.peek())
                    root = root.right;  // 第一次出栈
                else{
                    res.add(root.val);  // 第二次出栈
                    root = null;
                }
            }
            return res;
        }
    

    N叉树


    前序遍历

    实现N叉树的非递归前序遍历,我们同样可以利用这一数据,需要保证栈顶的节点就是我们当前的遍历节点。

    1. 将根节点入栈
    2. 如果栈不为空,弹出栈顶结点
        1)将值add到list中
        2)将所有的子节点逆序推入栈中
    3. 栈为空,返回前序遍历的结果list
    public List<Integer> preorder(Node root){
            Stack<Node> stack = new Stack<>();
            ArrayList<Integer> res = new ArrayList<>();
            if (root == null)
                return res;
            stack.push(root);
    
            while(!stack.isEmpty()){
                Node node = stack.pop();
                res.add(node.val);
                for (int i = node.children.size() - 1; i>=0; i--)
                    stack.add(node.children.get(i));     // 孩子节点倒着进栈
            }
            return res;
        }
    

    后序遍历

    使用两个栈,第一个栈将节点从上到下,从左到右压入,第二个栈存储后序遍历结果的倒叙,出栈即为后序遍历。

    1. 根节点进stack1
    2. 如果stack1不为空,栈顶节点出栈
        1)压入stack2
        2)孩子节点依次压入stack1
    3. stack2中的节点依次出栈,将值add到res中,返回res
    public List<Integer> postorder(Node root) {
            List<Integer> list = new ArrayList<>();
            if (root == null){
                return list;
            }
            Stack<Node> stack1 = new Stack<>();
            Stack<Node> stack2 = new Stack<>();
            stack1.push(root);
            while(!stack1.isEmpty()){
                Node node = stack1.pop();
                stack2.push(node);
                if (node.children != null){
                    for (Node child : node.children){
                        stack1.push(child);
                    }
                }
            }
            while (!stack2.isEmpty()){
                list.add(stack2.pop().val);
            }
            return list;
        }
    

    层序遍历

    层次遍历利用队列,需要保证当前队列是当前层次的节点,因此引入size控制list的大小。

    1. root进入队列
    2. 如果队列不为空
        1)记录当前层次的大小size
        2)依次弹出size个节点,每个节点将值add到list中后,其子节点入队列
        3)将某一层次的节点值list add到res中
    3. 返回结果
    public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> lists = new ArrayList<>();
            if (root == null)
                return lists;
            List<TreeNode> nodes = new LinkedList<>();
            nodes.add(root);
            while (!nodes.isEmpty()){
                int size = nodes.size();
                List<Integer> list = new ArrayList<>();
                for (int i = 0; i < size; i++){
                    TreeNode node = nodes.remove(0);
                    list.add(node.val);
                    // 二叉树
                    if (node.left != null)
                        nodes.add(node.left);
                    if (node.right != null)
                        nodes.add(node.right);
                    // N叉树
                  //for (Node child : node.children)
                  //    nodes.add(child);   
                }
                lists.add(list);
            }
            return lists;
    

    总结


    树的遍历无非两种方法:递归和非递归。递归框架化较简单,非递归一般都是采用栈来实现。在各种遍历中,后续遍历相对较难,二叉树采取节点重复进栈的方式,N叉树则是利用双栈,从上到下,从左到右进栈后,依次出栈再入另外一个栈,这个栈的出栈的结果即为后序遍历(或者是队列,最后翻转)。

    相关题目:

    94.二叉树的中序遍历
    102.二叉树的层序遍历
    144.二叉树的前序遍历
    145.二叉树的后序遍历
    429.N叉树的层序遍历
    589.N叉树的前续遍历
    590.N叉树的后续遍历

    TO BE CONTINUED...
  • 相关阅读:
    有关云计算的思考
    转载:args build and execute command lines from standard input
    云计算时代来临
    How to check the service availble
    反射小结
    JOptionPane用法java
    eclipse快捷键调试总结
    javascript 控制 html元素 显示/隐藏
    "ORA00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效"的快速解决方法
    麻辣豆腐与编码
  • 原文地址:https://www.cnblogs.com/ly-leah/p/13535382.html
Copyright © 2020-2023  润新知