zoukankan      html  css  js  c++  java
  • Leetcode Tags(13)Tree

      1.前序、中序、后序递归方式遍历二叉树

        public void preOrderRecur(Node T) {
            if (T != null) {
                System.out.print(T.val + " ");
                preOrderRecur(T.left);
                preOrderRecur(T.right);
            }
        }
    
        public void inOrderRecur(Node T) {
            if (T != null) {
                inOrderRecur(T.left);
                System.out.print(T.val + " ");
                inOrderRecur(T.right);
            }
        }
    
        public void postOrderRecur(Node T) {
            if (T != null) {
                postOrderRecur(T.left);
                postOrderRecur(T.right);
                System.out.print(T.val + " ");
            }
        }
    前、中、后序递归遍历二叉树

      2.前序非递归遍历二叉树

    • 创建一个栈stack,并将头结点T压入stack中
    • 从stack中弹出栈顶结点并访问,然后将栈顶结点的非空右孩子入栈,将栈顶结点的非空左孩子入栈
    • 重复上述过程,直到stack为空
        public void preOrderUnRecur(Node T) {
            if (T != null) {
                Stack<Node> stack = new Stack<>();
                stack.push(T);
                while (!stack.isEmpty()) {
                    T = stack.pop();
                    System.out.print(T.val + " ");
                    if (T.right != null) stack.push(T.right);
                    if (T.left  != null) stack.push(T.left);
                }
            }
        }

      分析执行过程:

                                    A
                                /       
                               B         C
                              /        /  
                             D    E   F    G
                            /         /       
                           H        I          J
                             
                              K
    结点A入栈,弹出并打印,C入栈、B入栈,此时栈:C B(栈顶)
    结点B弹出并打印,E入栈,D入栈,此时:C E D
    结点D弹出并打印,H入栈,此时:C E H
    结点H弹出并打印,K入栈,此时:C E K
    结点K弹出并打印,此时:C E
    结点E弹出并打印,此时:C
    结点C弹出并打印,G入栈,F入栈,此时:G F
    结点F弹出并打印,I入栈,此时:G I
    结点I弹出并打印,此时:G
    结点G弹出并打印,J入栈,此时:J
    结点J弹出并打印,此时:
    →A B D H K E C F I G J
    前序非递归遍历过程分析

      变体1:N-ary树前序遍历非递归

        public List<Integer> preOrderUnRecur(N_aryTreeNode root) {
            List<Integer> list = new ArrayList<>();
            if (root != null) {
                Stack<N_aryTreeNode> stack = new Stack<>();
                stack.push(root);
                while (!stack.isEmpty()) {
                    root = stack.pop();
                    list.add(root.val);
                    if (root.children != null) {
                        for (int i = root.children.size() - 1; i >= 0; i--) {
                            stack.push(root.children.get(i));
                        }
                    }
                }
            }
            return list;
        }

      3.中序非递归遍历二叉树

    • 创建一个栈对象,初始时T为根结点
    • T入栈,对于以T为头的整棵子树来说,依次把左边界入栈,即不断地令T=T.left
    • 不断重复上一步,直到T为null,此时弹出栈顶结点并打印,然后令T=T.right
    • 当栈为空且T为null时,整个过程执行完毕。
        public void inOrderUnRecur(Node T) {
            if (T != null) {
                Stack<Node> stack = new Stack<>();
                while (!stack.isEmpty() || T != null) {
                    if (T != null) {
                        stack.push(T);
                        T = T.left;
                    } else {
                        T = stack.pop();
                        System.out.print(T.val + " ");
                        T = T.right;
                    }
                }
            }
        }

      分析执行过程:

                                    A
                                /       
                               B         C
                              /        /  
                             D    E   F    G
                            /         /       
                           H        I          J
                             
                              K
    
    T为A,A入栈,此时:A,T为B
    T为B,B入栈,此时:A B,T为D
    T为D,D入栈,此时:A B D,T为H
    T为H,H入栈,此时:A B D H,T为null
    H出栈打印H,T为K,此时:A B D 
    K入栈,T为null
    K出栈打印K,T为null
    D出栈打印D,T为null
    B出栈打印B,T为E
    E入栈,T为null
    E出栈打印E,T为null
    A出栈打印A,T为C
    T为C,C入栈,此时:C,T为F
    T为F,F入栈,此时:C F,T为I
    T为I,I入栈,此时:C F I,T为null
    I出栈打印I,T为null
    F出栈打印F,T为null
    C出栈打印C,T为G
    T为G,G入栈,此时:G,T为null
    G出栈打印G,T为J
    T为J,J入栈,此时:J,T为null
    J出栈打印J,此时: ,并且T为null,程序停止
    →H K D B E A I F C G J 
    中序非递归遍历过程

      4.后序非递归遍历二叉树

    • 创建一个栈对象,将头结点T入栈,同时在整个流程中,T代表最近一次弹出并打印的结点,c代表栈顶结点,初始时T为头结点,c为null
    • 如果栈不为空,每次令c为栈顶结点但是不弹出,有下面的三种情况:
    1. 如果c的左孩子不为null,并且T不等于c的左孩子,也不等于c的右孩子,则把c的左孩子压入栈中。这是因为:由于T表示最近一次弹出并打印的结点,如果T等于栈顶结点c的左孩子或者是右孩子,说明c的左子树与右子树已经打印完毕,此时不应该将c的左孩子放入栈中。否则,说明c的左子树还没被处理过,那么此时将c的左孩子入栈。(每次都是左子树先入栈,如果c的右孩子等于T,那么说明左子树一定先于右子树之前已经访问完成,因此不用再管左子树,即不用入栈左子树)
    2. 如果条件1不成立,并且c的右孩子不为null,T不等于c的右孩子,则把c的右孩子入栈。这是因为:如果是T等于c的右孩子,说明c的右子树已经打印完毕,此时不应该再将c的右孩子放入栈中。否则,说明c的右子树还没被处理过,此时将c的右孩子入栈
    3. 如果条件1和条件2都不成立,说明c的左子树和右子树都已经打印完毕,从栈中弹出c,并且令T为c
    • 重复上述步骤,一致到栈空为止。
        public void postOrderUnRecur(Node T) {
            if (T != null) {
                Stack<Node> stack = new Stack<>();
                stack.push(T);
                Node c = null;
                while (!stack.isEmpty()) {
                    c = stack.peek();
                    if (c.left != null && T != c.left && T != c.right) {
                        stack.push(c.left);
                    } else if (c.right != null && T != c.right) {
                        stack.push(c.right);
                    } else {
                        System.out.print(stack.pop().val + " ");
                        T = c;
                    }
                }
            }
        }

      分析执行过程:

                                    A
                                /       
                               B         C
                              /        /  
                             D    E   F    G
                            /         /       
                           H        I          J
                             
                              K
    
    T为A,A入栈,此时:A,c=null
    T=A,c=A,1if:B入栈,此时:A B
    T=A,c=B,1if:D入栈,此时:A B D
    T=A,c=D,1if:H入栈,此时:A B D H
    T=A,c=H,2if:K入栈,此时:A B D H K
    T=A,c=K,3else:打印K,T为K,此时:A B D H
    T为K,c=H,3else:打印H,T为H,此时:A B D 
    T为H,c=D,3else:打印D,T为D,此时:A B
    T为D,c=B,2if:E入栈,此时:A E
    T为D,c=E,3else:打印E,T为E,此时:A
    T为E,c=A,2if:C入栈,此时:A C
    T为B,c=C,1if:F入栈,此时:A C F
    T为B,c=F,1if:I入栈,此时:A C F I
    T为B,c=I,3else:打印I,T为I,此时:A C F
    T为I,c=F,3else:打印F,T为F,此时:A C
    T为F,c=C,2if:G入栈,此时:A C G
    T为F,c=G,2if:J入栈,此时:A C G J
    T为F,c=J,3else,打印J,T为J,此时:A C G
    T为K,c=G,3else,打印G,T为G,此时:A C
    T为G,c=C,3else:打印C,T为C,此时:A
    T为C,c=A,3else:打印A,T为A,此时:
    →K H D E B I F J G C A
    后序非递归遍历过程

      变体0:后序递归遍历N-ary树

        public void postOrderTraverse(N_aryTreeNode root) {
            if (root != null) {
                if (root.children != null) {
                    for(N_aryTreeNode node : root.children) {
                        postOrderTraverse(node);
                    }
                }
                System.out.print(root.val + " ");
            }
        }

      变体1:后序非递归遍历N-ary树(注意到倒着入栈,例如3 2 4入栈时要按照4 2 3的顺序入栈)

      后序遍历结果为:5 6 3 2 4 1

      方法1:慢一点的方法

        public void postOrderUnRecur(N_aryTreeNode root) {
            if (root != null) {
                Stack<N_aryTreeNode> stack = new Stack<>();
                stack.push(root);
                N_aryTreeNode c = null;
                while (!stack.isEmpty()) {
                    c = stack.peek();
                    if (c.children != null && !c.children.contains(root)) { 
                        for (int i = c.children.size() - 1; i >= 0; i--) {
                            stack.push(c.children.get(i));
                        }
                    } else {
                        System.out.print(stack.pop().val + " ");
                        root = c;
                    }
                }
            }
        }

      方法二:更快的方法:巧妙地使用了Collections.reverse(list);这个技巧

        public List<Integer> postOrderUnRecur2(N_aryTreeNode root) {
            List<Integer> list = new ArrayList<>();
            if (root == null) return list;
            Stack<N_aryTreeNode> stack = new Stack<>();
            stack.add(root);
            while (!stack.isEmpty()) {
                root = stack.pop();
                list.add(root.val);
                if (root.children != null) {
                    for (N_aryTreeNode n_aryTreeNode : root.children) {
                        stack.add(n_aryTreeNode);
                    }
                }
            }
            Collections.reverse(list);
            return list;
        }

      5.层序非递归遍历二叉树(BFS)

    • 创建一个队列对象, 根结点入队列
    • 如果队列非空,则将队列头结点弹出并访问,然后将该结点的非空左孩子入队列,再将队列的非空右孩子入队列,
    • 重复上述过程
        public void levelOrderUnRecur(Node T) {
            if (T != null) {
                Queue<Node> queue = new ArrayDeque<>();
                queue.add(T);
                while (!queue.isEmpty()) {
                    T = queue.poll();
                    System.out.print(T.val + " ");
                    if (T.left != null)  queue.add(T.left);
                    if (T.right != null) queue.add(T.right);
                }
            }
        }

      分析执行过程:

                                    A
                                /       
                               B         C
                              /        /  
                             D    E   F    G
                            /         /       
                           H        I          J
                             
                              K
    
    A入队列,此时:A
    A出队列并打印,T为A,B入队列,C入队列,此时:C (队列头)
    B出队列并打印,T为B,D入队列,E入队列,此时:E D C
    C出队列并打印,T为C,F入队列,G入队列,此时:G F E D
    D出队列并打印,T为D,H入队列,此时:H G F E
    E出队列并打印,此时:H G F 
    F出队列并打印,I入队列,此时:I H G
    G出队列并打印,J入队列,此时:J I H 
    H出队列并打印,K入队列,此时:K J I
    I出队列并打印,此时:K J
    J出队列并打印,此时:K
    K出队列并打印,此时:
    →A B C D E F G H I J K
    层序非递归遍历过程

      变体:按照层数打印每一层的数据:

        public void printByLevel(Node root) {
            if (root == null) return;
            Queue<Node> queue = new LinkedList<>();
            int level  = 1;
            Node last  = root;
            Node nLast = null;
            queue.add(root);
            System.out.print("Level " + (level++) + " : ");
            while (!queue.isEmpty()) {
                root = queue.poll();
                System.out.print(root.val + " ");
                if (root.left != null) {
                    queue.add(root.left);
                    nLast = root.left;
                }
                if (root.right != null) {
                    queue.add(root.right);
                    nLast = root.right;
                }
                if (root == last && !queue.isEmpty()) {
                    System.out.print("
    Level " + (level++) + " : ");
                    last = nLast;
                }
            }
            System.out.println();
        }

      6.求二叉树的深度

       // (后序递归遍历算法)求二叉树的深度
        public int getBTDepth(BiTreeNode T) {
            if (T != null) {
                int lDepth = getBTDepth(T.lchild);
                int rDepth = getBTDepth(T.rchild);
                return 1 + (lDepth > rDepth ? lDepth : rDepth);
            }
            return 0;
        }

      求N-ary 树的最大深度

        public int maxDepth(Node root) {
            if (root != null) {
                int depth = 0;
                if (root.children != null) {
                    for (Node node : root.children) {
                        depth = Math.max(depth,maxDepth(node));
                    }
                }
                return 1 + depth;
            }
            return 0;
        }

      求二叉树的最小深度BFS:

        public int minDepth(TreeNode root) {
            if (root == null) return 0;
            int depth = 1;
            TreeNode last = root;
            TreeNode nLast = null;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            while (!queue.isEmpty()) {
                root = queue.poll();
                if (root.left == null && root.right == null) return depth;
                if (root.left  != null) {
                    queue.offer(root.left);
                    nLast = root.left;
                }
                if (root.right != null) {
                    queue.offer(root.right);
                    nLast = root.right;
                }
                if (root == last || queue.isEmpty()) {
                    last = nLast;
                    depth++;
                }
            }
            return depth;
        }

      求二叉树的最小深度DFS:

        public int minDepthDFS(TreeNode root) {
            if (root == null) return 0;
            int lDepth = minDepth(root.left);
            int rDepth = minDepth(root.right);
            return (lDepth == 0 || rDepth == 0) ? lDepth + rDepth + 1 : Math.min(lDepth, rDepth) + 1;
        }

      7.求二叉树结点的个数

      8.判断两棵二叉树是否相等

      如果值相等,那么如果左子树和右子树都相等,就返回true。

        public boolean isSameTree(TreeNode p, TreeNode q) {
            if (p == null && q == null) return true;
            if (p != null && q != null) {
                if (p.val == q.val) {
                    if (isSameTree(p.left, q.left)) {
                        if (isSameTree(p.right, q.right)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

      9.二叉树的查找

      10.二叉搜索树BST的查找(迭代和递归两种方法)

        public Node searchBSTIteration(Node root, int val) {
            while (root != null) {
                if (val == root.val) {
                    return root;
                } else if (val < root.val) {
                    root = root.left;
                } else {
                    root = root.right;
                }
            }
            return null;
        }
        
        public Node searchBSTRecursion(Node root, int val) {
            if (root == null) return root;
            if (root.val == val) {
                return root;
            } else {
                return val < root.val ? searchBSTRecursion(root.left, val) : searchBSTRecursion(root.right, val);
            }
        }

       11.寻找两棵树的叶结点以及判断两棵树的叶结点的值相等

        public boolean leafSimilar(TreeNode root1, TreeNode root2) {
            List<Integer> list1 = new ArrayList<>();
            List<Integer> list2 = new ArrayList<>();
            dfs(root1, list1);
            dfs(root2, list2);
            return list1.equals(list2);
        }
        
        private void dfs(TreeNode root, List<Integer> leafVal) {
            if (root != null) {
                if (root.left == null && root.right == null) leafVal.add(root.val);
                dfs(root.left, leafVal);
                dfs(root.right, leafVal);
            }
        }

       12.求左叶子的和

        public int sumOfLeftLeaves(TreeNode root) {
            int sum = 0;
            if (root != null) {
                Stack<TreeNode> stack = new Stack<>();
                stack.push(root);
                while (!stack.isEmpty()) {
                    root = stack.pop();
                    if (root.right != null) stack.push(root.right);
                    if (root.left != null) {
                        stack.push(root.left);
                        if (root.left.left == null && root.left.right == null) {
                            sum += root.left.val;
                        }
                    }
                }
            }
            return sum;
        }
  • 相关阅读:
    代码块;继承;this与super关系;重载与重写对比;类的继承特点;final关键字 (Java Day08)
    变量访问;this关键字;静态;工具类;帮助文档;Math使用;Arrays使用(Java Day07)
    面向对象;类和对象;访问对象;创建对象在内存中的理解;匿名对象;封装和this (Java Day06)
    如何保证 RocketMQ 不丢失消息
    Java-String类型的参数传递问题
    图解前中后序遍历
    一文彻底理解ReentrantLock可重入锁的使用
    聊聊MySQL、HBase、ES的特点和区别
    MySQL vs Java 数据类型
    Multi-Tenancy多租户模式
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9581234.html
Copyright © 2011-2022 走看看