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:
- What is preorder? (parent node is processed before its children)
- 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:
- The order of "inorder" is: left child -> parent -> right child
- Use a stack to track nodes
- Understand when to push node into the stack and when to pop node out of the stack
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.right;C)当从左而上,第一次经过父节点,但父节点没有右子树时,prev == curr.left,我们需要遍历节点。
见程序,程序来自Program creek
The key to to iterative postorder traversal is the following:
- The order of "Postorder" is: left child -> right child -> parent node.
- Find the relation between the previously visited node and the current node
- 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 }
尚待实践和整理