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为栈顶结点但是不弹出,有下面的三种情况:
- 如果c的左孩子不为null,并且T不等于c的左孩子,也不等于c的右孩子,则把c的左孩子压入栈中。这是因为:由于T表示最近一次弹出并打印的结点,如果T等于栈顶结点c的左孩子或者是右孩子,说明c的左子树与右子树已经打印完毕,此时不应该将c的左孩子放入栈中。否则,说明c的左子树还没被处理过,那么此时将c的左孩子入栈。(每次都是左子树先入栈,如果c的右孩子等于T,那么说明左子树一定先于右子树之前已经访问完成,因此不用再管左子树,即不用入栈左子树)
- 如果条件1不成立,并且c的右孩子不为null,T不等于c的右孩子,则把c的右孩子入栈。这是因为:如果是T等于c的右孩子,说明c的右子树已经打印完毕,此时不应该再将c的右孩子放入栈中。否则,说明c的右子树还没被处理过,此时将c的右孩子入栈
- 如果条件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("\nLevel " + (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; }
儿女情长什么的,最影响我们闯荡江湖了。