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 }