zoukankan      html  css  js  c++  java
  • Java数据结构四之——二叉树的前、中、后序遍历

    程序来自Program Creek

    前序遍历:

    Preorder binary tree traversal is a classic interview problem about trees. The key to solve this problem is to understand the following:

    1. What is preorder? (parent node is processed before its children)
    2. Use Stack from Java Core library

    It is not obvious what preorder for some strange cases. However, if you draw a stack and manually execute the program, how each element is pushed and popped is obvious.

    The key to solve this problem is using a stack to store left and right children, and push right child first so that it is processed after the left child.

     1 public class TreeNode {
     2     int val;
     3     TreeNode left;
     4     TreeNode right;
     5     TreeNode(int x) { val = x; }
     6 }
     7  
     8 public class Solution {
     9     public ArrayList<Integer> preorderTraversal(TreeNode root) {
    10         ArrayList<Integer> returnList = new ArrayList<Integer>();
    11  
    12         if(root == null)
    13             return returnList;
    14  
    15         Stack<TreeNode> stack = new Stack<TreeNode>();
    16         stack.push(root);
    17  
    18         while(!stack.empty()){
    19             TreeNode n = stack.pop();
    20             returnList.add(n.val);
    21  
    22             if(n.right != null){
    23                 stack.push(n.right);
    24             }
    25             if(n.left != null){
    26                 stack.push(n.left);
    27             }
    28  
    29         }
    30         return returnList;
    31     }
    32 }

       以上程序的思想类似于深度优先搜索(DFS)了,只不过在这里是先遍历当前节点,然后左边优先遍历了!

      由于我是先看的后序遍历,因此,自己实现的前序遍历便按照后序遍历的样子写的。程序如下:

     1 public class Solution {
     2     public List<Integer> preorderTraversal(TreeNode root) {
     3         ArrayList<Integer> result = new ArrayList<Integer>();
     4  
     5         if(root == null)
     6             return result; 
     7  
     8         Stack<TreeNode> stack = new Stack<TreeNode>();
     9         stack.push(root);
    10  
    11         TreeNode prev = null;
    12         while(!stack.empty()){
    13             TreeNode curr = stack.peek();
    14             
    15             if(prev==null||curr==prev.left||curr==prev.right){
    16                 
    17                  result.add(curr.val);
    18                  
    19                  if(curr.left!= null){
    20                     stack.push(curr.left);
    21                 }else if(curr.right!=null){
    22                     stack.push(curr.right);
    23                 }
    24                 else{
    25                     stack.pop();
    26                 }
    27             }else if(prev==curr.left){
    28                 if(curr.right!=null){
    29                     stack.push(curr.right);
    30                 }
    31                 else{
    32                     stack.pop();
    33                 }
    34             }else if(prev==curr.right){
    35                 stack.pop();
    36             }
    37             
    38             prev = curr;
    39         }
    40         return result;
    41     }
    42 }

     九章算法中的各种版本答案

     1 Version 0: Non-Recursion (Recommend)
     2 /**
     3  * Definition for binary tree
     4  * public class TreeNode {
     5  *     int val;
     6  *     TreeNode left;
     7  *     TreeNode right;
     8  *     TreeNode(int x) { val = x; }
     9  * }
    10  */
    11 public class Solution {
    12     public List<Integer> preorderTraversal(TreeNode root) {
    13         Stack<TreeNode> stack = new Stack<TreeNode>();
    14         List<Integer> preorder = new ArrayList<Integer>();
    15         
    16         if (root == null) {
    17             return preorder;
    18         }
    19         
    20         stack.push(root);
    21         while (!stack.empty()) {
    22             TreeNode node = stack.pop();
    23             preorder.add(node.val);
    24             if (node.right != null) {
    25                 stack.push(node.right);
    26             }
    27             if (node.left != null) {
    28                 stack.push(node.left);
    29             }
    30         }
    31         
    32         return preorder;
    33     }
    34 }
    35 
    36 //Version 1: Traverse
    37 public class Solution {
    38     public ArrayList<Integer> preorderTraversal(TreeNode root) {
    39         ArrayList<Integer> result = new ArrayList<Integer>();
    40         traverse(root, result);
    41         return result;
    42     }
    43 
    44     private void traverse(TreeNode root, ArrayList<Integer> result) {
    45         if (root == null) {
    46             return;
    47         }
    48 
    49         result.add(root.val);
    50         traverse(root.left, result);
    51         traverse(root.right, result);
    52     }
    53 }
    54 
    55 //Version 2: Divide & Conquer
    56 public class Solution {
    57     public ArrayList<Integer> preorderTraversal(TreeNode root) {
    58         ArrayList<Integer> result = new ArrayList<Integer>();
    59         // null or leaf
    60         if (root == null) {
    61             return result;
    62         }
    63 
    64         // Divide
    65         ArrayList<Integer> left = preorderTraversal(root.left);
    66         ArrayList<Integer> right = preorderTraversal(root.right);
    67 
    68         // Conquer
    69         result.add(root.val);
    70         result.addAll(left);
    71         result.addAll(right);
    72         return result;
    73     }
    74 }

    中序遍历:

    The key to solve inorder traversal of binary tree includes the following:

    1. The order of "inorder" is: left child -> parent -> right child
    2. Use a stack to track nodes
    3. Understand when to push node into the stack and when to pop node out of the stack

    Binary-Tree-Inorder-Traversal-in-Java

     1 //Definition for binary tree
     2 public class TreeNode {
     3      int val;
     4      TreeNode left;
     5      TreeNode right;
     6      TreeNode(int x) { val = x; }
     7  }
     8  
     9 public class Solution {
    10     public ArrayList<Integer> inorderTraversal(TreeNode root) {
    11         // IMPORTANT: Please reset any member data you declared, as
    12         // the same Solution instance will be reused for each test case.
    13          ArrayList<Integer> lst = new ArrayList<Integer>();
    14  
    15         if(root == null)
    16             return lst; 
    17  
    18         Stack<TreeNode> stack = new Stack<TreeNode>();
    19         //define a pointer to track nodes
    20         TreeNode p = root;
    21  
    22         while(!stack.empty() || p != null){
    23  
    24             // if it is not null, push to stack
    25             //and go down the tree to left
    26             if(p != null){
    27                 stack.push(p);
    28                 p = p.left;
    29  
    30             // if no left child
    31             // pop stack, process the node
    32             // then let p point to the right
    33             }else{
    34                 TreeNode t = stack.pop();
    35                 lst.add(t.val);
    36                 p = t.right;
    37             }
    38         }
    39  
    40         return lst;
    41     }
    42 }

       依旧根究后续遍历的总体思路来进行遍历的话,分区什么时候向下走,什么时候遍历,左上,右上的情况,就非常好遍历了。这种思想还是比较好,比较通用的。程序如下:

     1 public class Solution {
     2     public List<Integer> inorderTraversal(TreeNode root) {
     3          ArrayList<Integer> result = new ArrayList<Integer>();
     4  
     5         if(root == null)
     6             return result; 
     7  
     8         Stack<TreeNode> stack = new Stack<TreeNode>();
     9         stack.push(root);
    10  
    11         TreeNode prev = null;
    12         while(!stack.empty()){
    13             TreeNode curr = stack.peek();
    14             if(prev==null||curr==prev.left||curr==prev.right){
    15                 if(curr.left!=null){
    16                     stack.push(curr.left);
    17                 }else if(curr.right!=null){
    18                     result.add(curr.val);
    19                     stack.push(curr.right);
    20                 }else{
    21                      result.add(curr.val);
    22                      stack.pop();
    23                 }
    24             }else if(prev==curr.left){
    25                 result.add(curr.val);
    26                 if(curr.right!=null){
    27                     stack.push(curr.right);
    28                 }else{
    29                     stack.pop();
    30                 }
    31                 
    32             }else if(prev==curr.right){
    33                 stack.pop();
    34             }
    35             prev=curr;
    36         }//while
    37         return result;
    38     }
    39 }

    后序遍历:

    递归算法:

      递归算法很简单。程序来自九章算法

     1 //Recursive
     2 public ArrayList<Integer> postorderTraversal(TreeNode root) {
     3     ArrayList<Integer> result = new ArrayList<Integer>();
     4 
     5     if (root == null) {
     6         return result;
     7     }
     8 
     9 
    10     result.addAll(postorderTraversal(root.left));
    11     result.addAll(postorderTraversal(root.right));
    12     result.add(root.val);
    13 
    14     return result;   
    15 }

    迭代解法:

       使用两个指针prev和curr分别指向当前和上次访问的节点。

    总体原则遵循

      1.先经过左子树,遍历之

      2.经过父节点(此时是从左上来的),获得右节点

      3.经过右子树,遍历之

      4.再次经过父节点(此时是从右上来的)

      5.遍历父节点

    首先明确以下事实:

      当向左一直延伸时,curr始终在prev的前方(下面),也即满足如下条件:curr==prev.left当向右走的时候,满足:curr == prev.right

      从左端上来的时候满足prev == curr.left相应的当从右端上的时候满足prev == curr.right。 

    遍历步骤

      首先,我们向下走,沿着路径向左延伸直至左叶子或者没有左子节点的节点为止,对应①。当子树遍历完,才会向上走,当左节点遍历完或者左子树遍历完会从左向上走,此时对应③;此时“第一次经过父节点”,如果右子树不为空,获取右子树(将右子节点压栈),然后向下走,对应②,经过右子树(当便遍历右子树时,仍然先从左子子树下手,此时便循环上述①过程)。当右子树遍历完成之后,从右而上,对应④。此时是“第二次经过父节点”,遍历父节点。继续向上走,继续判断是从左而上③,还是从右而上④。

      有了以上思路,程序就好写了。我们只需知道判断条件:何时需要向下走,何时需要遍历节点,即可书写while循环遍历二叉树了。

    判断条件:

      这也很明确了,A)当初始时刻prev==null时;当遍历完左节点,第一次经过父节点时,curr == prev.left;当遍历完左子树,第一次经过父节点,并且父节点的右子树不为空时,curr == prev.right,我们需要向下走。

      AA)curr == prev.right,并且无左右子节点时;B)当从右而上,右子树遍历完成,第二次经过父节点时,prev==curr.rightC)当从左而上,第一次经过父节点,但父节点没有右子树时,prev == curr.left,我们需要遍历节点。

    见程序,程序来自Program creek

    The key to to iterative postorder traversal is the following:

    1. The order of "Postorder" is: left child -> right child -> parent node.
    2. Find the relation between the previously visited node and the current node
    3. Use a stack to track nodes

    As we go down the tree, check the previously visited node. If it is the parent of the current node, we should add current node to stack. When there is no children for current node, pop it from stack. Then the previous node become to be under the current node for next loop.

     1 //Definition for binary tree
     2 public class TreeNode {
     3     int val;
     4     TreeNode left;
     5     TreeNode right;
     6     TreeNode(int x) { val = x; }
     7 }
     8  
     9  
    10 public class Solution {
    11     public ArrayList<Integer> postorderTraversal(TreeNode root) {
    12  
    13         ArrayList<Integer> lst = new ArrayList<Integer>();
    14  
    15         if(root == null)
    16             return lst; 
    17  
    18         Stack<TreeNode> stack = new Stack<TreeNode>();
    19         stack.push(root);
    20  
    21         TreeNode prev = null;
    22         while(!stack.empty()){
    23             TreeNode curr = stack.peek();
    24  
    25             // go down the tree.
    26             //check if current node is leaf, if so, process it and pop stack,
    27             //otherwise, keep going down
    28             if(prev == null || prev.left == curr || prev.right == curr){
    29                 //prev == null is the situation for the root node
    30                 if(curr.left != null){
    31                     stack.push(curr.left);
    32                 }else if(curr.right != null){
    33                     stack.push(curr.right);
    34                 }else{
    35                     stack.pop();
    36                     lst.add(curr.val);
    37                 }
    38  
    39             //go up the tree from left node    
    40             //need to check if there is a right child
    41             //if yes, push it to stack
    42             //otherwise, process parent and pop stack
    43             }else if(curr.left == prev){
    44                 if(curr.right != null){
    45                     stack.push(curr.right);
    46                 }else{
    47                     stack.pop();
    48                     lst.add(curr.val);
    49                 }
    50  
    51             //go up the tree from right node 
    52             //after coming back from right node, process parent node and pop stack. 
    53             }else if(curr.right == prev){
    54                 stack.pop();
    55                 lst.add(curr.val);
    56             }
    57  
    58             prev = curr;
    59         }
    60  
    61         return lst;
    62     }
    63 }

    尚待实践和整理

  • 相关阅读:
    若没有任何实例包含Class Body 则enum被隐式声明为final
    Effective Java —— 多字段下考虑使用建造者模式构建实例
    Effective Java —— 用静态工厂方法代替构造器
    Java动态代理和CGLib代理
    Netty + Spring + ZooKeeper搭建轻量级RPC框架
    Netty学习摘记 —— UDP广播事件
    Netty学习摘记 —— 简单WEB聊天室开发
    Netty学习摘记 —— 心跳机制 / 基于分隔符和长度的协议
    Bugku 杂项 这是一张单纯的图片
    Bugku 杂项 签到题
  • 原文地址:https://www.cnblogs.com/LolaLiu/p/3989251.html
Copyright © 2011-2022 走看看