二叉树遍历

1. 定义

分三种:

  1. 先序遍历:先访问根节点,然后是左孩子,然后是右孩子(根,左,右)
  2. 中序遍历:左,根,右
  3. 后序遍历:左,右,根
  4. 层次遍历:从根节点开始,从上至下逐层遍历,同一层中,按从左至右顺序遍历

2. 递归解法

树表现为一种链表结构,链表问题大都可以采用递归实现。树更是常常有递归解法。

先、中、后遍历的递归写法同定义一致,再次不在赘述,可参照后边的代码理解。

层次遍历用递归好像不太直观吧,一般都是用队列迭代,具体也在下边介绍。

3. 非递归解法---迭代法

递归解法一般都对应有非递归解法(用栈来模拟递归过程,实现迭代)。

并非所有程序设计语言都允许递归;此外,大量递归可能造成栈溢出,所以有时需要提供非递归解法,即迭代解法,效率更高一些。

先、中、后遍历,在遍历节点时经过的路径其实是一样的,只是访问时机不同:从根节点沿左子树深入下去,深入不下去就返回,再从刚才深入时的右子树开始,继续之前的深入和返回。直到最后从根节点的右子树返回根节点为止

  1. 先序遍历:深入时,遇到节点就访问-------节点不空,入栈访问,遍历左子树;否则遍历右子树
  2. 中序遍历:从左子树返回时访问-----------节点不空,遍历左子树;否则出栈访问,遍历右子树
  3. 后续遍历:从右子树返回时访问-----------节点不空,遍历左子树;否则,如果是从左子树返回,遍历右子树,如果是从右子树返回,出栈访问

中序和后序看着差不多,而实现起来后序遍历略复杂(可能因为中序遍历完右子树就结束了,没有操作了,但是后序遍历完左子树和右子树都有操作,而且操作还不同),需要标志是否右子树都被访问了。可以对每个节点加一个标志位,标志右子树是否被访问了,也可以用一个变量来表示,具体看实现代码。

层次遍历:采用队列,将根节点入队列。队列不空时,取队首节点访问,并将队首节点的所有子节点入队列。直至队列为空。

参考资料:http://www.cztvu.ah163.net/czcai/soft/kj/sjjg/jj/ch6.htm

代码:

  1 package BinaryTree;
  2 
  3 import java.util.ArrayDeque;
  4 import java.util.Deque;
  5 import java.util.List;
  6 import java.util.Scanner;
  7 
  8 /**
  9  * @author 
 10  * @date 2015年9月16日
 11  */
 12 
 13 class TreeNode{
 14     public int val;
 15     public TreeNode left;
 16     public TreeNode right;
 17     
 18     public TreeNode( int val ){
 19         this.val = val;
 20     }
 21     
 22     public static TreeNode createTree(){
 23         Scanner in = new Scanner( System.in );
 24         TreeNode root = null;
 25         int val = in.nextInt();
 26         if( val != '#' ){
 27             root = new TreeNode(val);
 28             root.left = createTree();
 29             root.right = createTree();
 30         }
 31         in.close();
 32         return root;
 33     }
 34     
 35     //the recursion version of preOrder
 36     public void preOrderRec( TreeNode root, List<Integer> res ){
 37         if( root != null ){
 38             res.add(root.val);
 39             preOrderRec( root.left, res );
 40             preOrderRec( root.right, res );
 41         }
 42     }
 43     
 44     //the recursion version of inOrder
 45     public void inOrderRec( TreeNode root, List<Integer> res ){
 46         if( root != null ){
 47             inOrderRec( root.left, res );
 48             res.add( root.val );
 49             inOrderRec( root.right, res );
 50         }
 51     }
 52     
 53     //the recursion version of postOrder
 54     public void postOrderRec( TreeNode root, List<Integer> res ){
 55         if( root != null ){
 56             postOrderRec( root.left, res );
 57             res.add( root.val );
 58             postOrderRec( root.right, res );
 59         }
 60     }
 61     
 62     /*
 63      * the non-recursion version of preOrder using stack.  
 64      * When present node (present root) is not null, push and traverse left subtree.
 65      * Visit when push.
 66      * When present node (present root) is null, pop and traverse right subtree.
 67      */
 68     public void preOrder( TreeNode root, List<Integer> res ){
 69         Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
 70         TreeNode p = root;
 71         while( p!=null || !stack.isEmpty() ){
 72             if( p != null ){
 73                 stack.push(p);
 74                 //visit root-----1
 75                 res.add(p.val);
 76                 //traverse the left subTree-----2
 77                 p = p.left;
 78             }
 79             else{
 80                 p = stack.pop();
 81                 //traverse the right subTree-----3
 82                 p = p.right;
 83             }
 84         }
 85     }
 86     
 87     //the non-recursion version of inOrder using stack. Similar to preOrder except that it visits when pop.
 88     public void inOrder( TreeNode root, List<Integer> res ){
 89         Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
 90         TreeNode p = root;
 91         while( p != null || !stack.isEmpty() ){
 92             if( p != null ){
 93                 stack.push(p);
 94                 //traverse the left subtree------1
 95                 p = p.left;
 96             }
 97             else{
 98                 p = stack.pop();
 99                 //visit the root------2
100                 res.add(p.val);
101                 //traverse the right subtree-----3
102                 p = p.right;
103             }
104         }
105     }
106     
107     /*
108      * the non-recursion version of postOrder using stack. This is more complicated. It only visits when return from the right subree.
109      * When present node (present root) is not null, push and traverse the left subtree
110      * When present node (present root) is null, pop.
111      *     (1)If only the left subtree is visited, then push the right subtree.
112      *     (2)Else, that is, both the left and right subtree are visited, then visit when pop.
113      * 
114      * First implementation (after pushed, the root is in fact traversed twice: once when return from left, once when return from right.
115      * So we can try to push and pop every root twice):
116      * For the above (1) step, push the present root again and traverse the right subtree. 
117      * For the above (2) step, visit when the second pop.
118      */
119     public void postOrder1( TreeNode root, List<Integer> res ){
120         Deque<TreeNodeWithTag> stack = new ArrayDeque<TreeNodeWithTag>();
121         TreeNode p = root;
122         while( p != null || !stack.isEmpty() ){
123             if( p != null ){
124                 //first push
125                 stack.push( new TreeNodeWithTag( p, false) );
126                 //traverse the left subtree-----1
127                 p = p.left;
128             }
129             else{
130                 //pop
131                 TreeNodeWithTag top = stack.pop();
132                 if( !top.tag ){
133                     //second push
134                     top.tag = true;
135                     stack.push(top);
136                     //traverse the right subtree------2
137                     p = top.node.right;
138                 }
139                 else{
140                     //visit the root------3
141                     res.add(top.node.val);
142                 }
143             }
144         }
145     }
146     
147     /*
148      * The second implementation of postOrder. Similar to postOrder1 using the assistant tag for every node.
149      * When the present node is not null, push to traverse the left nodes;
150      * If present node is null, if top.right has not been visited, traverse the right subtree
151      * Else if top.right has been visited, pop and visit the root
152      */
153     public void postOrder2( TreeNode root, List<Integer> res ){
154         Deque<TreeNodeWithTag> stack = new ArrayDeque<TreeNodeWithTag>();
155         TreeNode p = root;
156         while( p != null || !stack.isEmpty() ){
157             while( p != null ){
158                 //traverse the left nodes
159                 stack.push( new TreeNodeWithTag( p, false) );
160                 p = p.left;
161             }
162             if( !stack.isEmpty() && !stack.peek().tag ){
163                 //traverse the right nodes
164                 stack.peek().tag = true;
165                 p = stack.peek().node.right;
166             }
167             else if( !stack.isEmpty() ){
168                 //visit the root
169                 res.add( stack.pop().node.val );
170             }
171         }
172     }
173     /*
174      * The third implementation of postOrder. Use only one tag to check the previous condition.
175      * If no return, traverse the left subtree
176      * Else if return from left, traverse the right subtree
177      * else if return from right, visit the root
178      */
179     public void postOrder3( TreeNode root, List<Integer> res ){
180         Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
181         boolean rightVisited = false;
182         TreeNode p = root;
183         while( p != null || !stack.isEmpty() ){
184             if( p != null ){
185                 //traverse the left subtree
186                 stack.push(p);
187                 p = p.left;
188                 rightVisited = false;
189             }
190             else if( !rightVisited ){
191                 //traverse the right subtree
192                 p = stack.peek().right;
193                 rightVisited = true;
194             }
195             else if( rightVisited ){
196                 //visit the root
197                 TreeNode top = stack.pop();
198                 res.add( top.val );
199                 if( !stack.isEmpty() && stack.peek().left == top )
200                     rightVisited = false;
201             }            
202         }
203     }
204     
205     /*
206      * The fourth implementation of postOrder. Use only one tag to check the previous condition.
207      */
208     public void postOrder4( TreeNode root, List<Integer> res ){
209         Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
210         boolean rightVisited = false;
211         TreeNode p = root;
212         while( p!=null || !stack.isEmpty() ){
213             if( p!=null ){
214                 //traverse the left subtree
215                 do{
216                     stack.push(p);
217                     p = p.left;                    
218                 }while( p!=null );
219                 rightVisited = false;
220             }
221             if( !rightVisited ){
222                 //traverse the right subtreee
223                 p = stack.peek().right;
224                 rightVisited = true;
225             }
226             else{
227                 //visit the root
228                 TreeNode top = stack.pop();
229                 res.add( top.val );
230                 if( !stack.isEmpty() && stack.peek().left == top )
231                     rightVisited = false;
232             }
233         }
234     }
235     //level order. Use queue.
236     public void levelOrder( TreeNode root, List<Integer> res ){
237         Deque<TreeNode> queue = new ArrayDeque<TreeNode>();
238         queue.add(root);
239         while( !queue.isEmpty() ){
240             TreeNode p = queue.remove();
241             res.add(p.val);    //visit up level
242             if( p.left != null )    queue.add(p.left);
243             if( p.right != null )    queue.add(p.right);
244         }
245     }
246     
247 }
248 
249 //used by postOrder1 and postOrder to tag if the left subtree or the right subtree is visited.
250 class TreeNodeWithTag{
251     TreeNode node;
252     boolean tag;    //false represents return from left subtree (the first push and pop), true represents return from the right subtree (the second push and pop)
253     public TreeNodeWithTag( TreeNode node, boolean value ){
254         this.node = node;
255         this.tag = value;        
256     }
257 }
View Code

原文地址:https://www.cnblogs.com/hf-cherish/p/4821681.html