145.Binary Tree Postorder Traversal---二叉樹後序非遞歸遍歷

題目連接ide

題目大意:後序遍歷二叉樹。post

法一:普通遞歸,只是這裏須要傳入一個list來存儲遍歷結果。代碼以下(耗時1ms):spa

 1     public List<Integer> postorderTraversal(TreeNode root) {
 2         List<Integer> list = new ArrayList<Integer>();
 3         list = dfs(root, list);
 4         return list;
 5     }
 6     public static List<Integer> dfs(TreeNode root, List<Integer> list) {
 7         if(root == null) {
 8             return list;
 9         }
10         else {
11             list = dfs(root.left, list);
12             list = dfs(root.right, list);
13             list.add(root.val);
14             return list;
15         }
16     }
View Code

法二(借鑑):後序遍歷順序是「左右根」,這裏將其反過來遍歷,也就是「根右左」,而後將遍歷結果反轉返回便可。這裏用到了LinkedList.addFirst()方法,即將值插到鏈表頭部。(addLast()方法與add()方法同樣是插到鏈表尾部)。這裏也能夠用ArrayList.add(),在最後再調用Collections.reverse(list)方法便可。此方法代碼簡單,但不是很好想。代碼以下(耗時1ms):code

 1     public List<Integer> postorderTraversal(TreeNode root) {
 2         LinkedList<Integer> list = new LinkedList<Integer>();
 3         if(root == null) {
 4             return list;
 5         }
 6         Stack<TreeNode> stack = new Stack<TreeNode>();
 7         stack.push(root);
 8         TreeNode tmp = null;
 9         while(!stack.isEmpty()) {
10             tmp = stack.pop();
11             list.addFirst(tmp.val);
12             if(tmp.left != null) {
13                 stack.push(tmp.left);
14             }
15             if(tmp.right != null) {
16                 stack.push(tmp.right);
17             }
18         }
19         return list;
20     }
View Code

法三(借鑑):普通後序非遞歸遍歷,這裏用一個輔助棧來標記結點是否已經訪問右結點,若是已經訪問右結點,則將根值加入list中,不然訪問右結點壓棧。由於有兩個棧要壓棧出棧,耗時較長。也能夠在TreeNode結點中加入一個標記屬性flag來標記是否訪問過右結點,這樣就不須要輔助棧了,時間應該會快一些。代碼以下(耗時2ms):blog

 1     public List<Integer> postorderTraversal(TreeNode root) {
 2         List<Integer> list = new ArrayList<Integer>();
 3         if(root == null) {
 4             return list;
 5         }
 6         Stack<TreeNode> stackNode = new Stack<TreeNode>();
 7         //0表示右結點未訪問,1表示右結點已訪問
 8         Stack<Integer> stackFlag = new Stack<Integer>();
 9         stackNode.push(root);
10         stackFlag.push(0);
11         TreeNode tmp = root.left;//已壓棧,則訪問其左結點
12         while(!stackNode.isEmpty()) {
13             while(tmp != null) {
14                 stackNode.push(tmp);
15                 stackFlag.push(0);
16                 tmp = tmp.left;
17             }
18             if(stackFlag.peek() == 0) {
19                 //右結點未訪問,則訪問右結點
20                 tmp = stackNode.peek().right;
21                 stackFlag.pop();
22                 stackFlag.push(1);//將訪問右結點狀態置1
23             }
24             else {
25                 //右結點已訪問,則將根結點加入list隊列,並將根節點彈出
26                 list.add(stackNode.pop().val);
27                 stackFlag.pop();//彈出根節點狀態值
28             }
29         }
30         return list;
31     }
View Code

法四(借鑑):保證根結點在左孩子和右孩子訪問以後才能訪問,所以對於任一結點P,先將其入棧。若是P不存在左孩子和右孩子,則能夠直接訪問它;或者P存 在左孩子或者右孩子,可是其左孩子和右孩子都已被訪問過了,則一樣能夠直接訪問該結點。若非上述兩種狀況,則將P的右孩子和左孩子依次入棧,這樣就保證了 每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。這個比法二還要難理解,特別是要先壓right再壓left。代碼以下(耗時2ms):遞歸

 1     public List<Integer> postorderTraversal(TreeNode root) {
 2         List<Integer> list = new ArrayList<Integer>();
 3         if(root == null) {
 4             return list;
 5         }
 6         Stack<TreeNode> stack = new Stack<TreeNode>();
 7         stack.push(root);
 8         TreeNode pre = null, cur = null;
 9         while(!stack.isEmpty()) {
10             cur = stack.peek();//判斷當前結點狀況,因此用peek不用pop
11             if((cur.left == null && cur.right == null) || 
12                 (pre != null && (pre == cur.left || pre == cur.right))) {
13                 //若是當前結點沒有左右孩子則直接彈出當前結點
14                 //若是當前結點的左右孩子都已經訪問完則彈出當前結點
15                 list.add(cur.val);
16                 pre = cur;
17                 stack.pop();
18             }
19             else {
20                 //注意這裏必定要先壓right再壓left,由於棧的先進後出的原則,到時候會先彈出left再彈出right,這樣的順序才正確。
21                 if(cur.right != null) {
22                     stack.push(cur.right);
23                 }
24                 if(cur.left != null) {
25                     stack.push(cur.left);
26                 }
27             }
28         }
29         return list;
30     }
View Code

 法五(借鑑):最接近先序、中序非遞歸遍歷的方法,先壓左結點再判斷棧頂元素。代碼以下(耗時2ms):隊列

 1 public List<Integer> postorderTraversal(TreeNode root) {
 2         List<Integer> list = new ArrayList<Integer>();
 3         if(root == null) {
 4             return list;
 5         }
 6         Stack<TreeNode> stack = new Stack<TreeNode>();
 7         stack.push(root);
 8         TreeNode pre = null, cur = root.left;
 9         while(!stack.isEmpty()) {
10             while(cur != null) {
11                 stack.push(cur);
12                 cur = cur.left;
13             }
14             //判斷棧頂結點
15             cur = stack.peek();
16             //判斷是否訪問棧頂結點
17             if(cur.right != null && pre != cur.right) {
18                 //若是不是從右孩子返回,即還未訪問右孩子,則訪問
19                 cur = cur.right;
20             }
21             else {
22                 //若是沒有右孩子或右孩子已經訪問過,則直接彈出當前節點進行訪問
23                 list.add(cur.val);
24                 stack.pop();
25                 //記錄當前訪問的結點
26                 pre = cur;
27                 //將當前結點賦空
28                 cur = null;
29             }
30         }
31         return list;
32     }
View Code
相關文章
相關標籤/搜索