思路
上一篇博文咱們講了二叉樹的遞歸算法,這裏咱們來寫一波二叉樹的非遞歸算法java
爲何說遍歷二叉樹能夠用遞歸node
二叉樹每一個結點都知足某個遍歷次序,而後從根結點開始遍歷,這個流程很是知足遞歸的模型,就是一個大的問題按照某種方式能夠劃分爲許多細小的問題,而後這些細小的問題又能夠用一樣的方式繼續劃分,直到爲空或者說直到有個出口算法
全部遞歸能夠轉化成非遞歸數組
那非遞歸我該如何實現呢?這就不得不從遞歸的有一個特色提及了,遞歸老是這樣的形式:a<b<c<d
,a 表示第一層方法,b 表示第二層的遞歸,c 表示第三層的遞歸,d 表示第四層的遞歸,那麼咱們程序運行的效果確定是:a 到 b 到 c 到 d,而後 d 找到出口所有執行完了,返回 c,c 又所有執行完了,再到 b,b 所有執行完了,再到 a,也就是 a→b→c→d→d→c→b→a 的次序,這不就是棧的數據結構嗎???對了,就是棧,所以若是咱們不用遞歸,那咱們確定得用循環再加上棧的使用數據結構
先序&中序和後序有什麼不一樣ide
咱們知道先序是根左右,中序是左根右,後序是左右根,先序和中序沒啥大的花樣,可是後續有些不一樣,爲何呢?由於對於先序遍從來講,最早記錄結點的數據,而後能找到左結點就一直找左結點,找不到能夠再找棧頂的右結點,並同時釋放棧頂結點;若是是中序的話,先找左結點,找不到爲止,就記錄棧頂結點的值,釋放棧頂結點,同時再去找該棧頂結點的右結點;可是對後序遍歷可不一樣了,由於後序是先去找左結點,一直到找不到爲止,咱們再去找棧頂的右結點,可是要注意的是後序遍歷在左右結點轉換的時候,咱們並不知道這個根結點何時彈出棧頂,最大的不一樣就在於這裏。先序和中序在左結點找不到時切換右結點時候會彈出根結點,根結點這顆棋子已經沒有用了,可是後序遍歷可不這樣,當左結點實在找不到時候,去找棧頂的右結點,此時棧頂結點還不能獲得釋放,由於右結點的後序遍歷沒有找盡每個結點,因此還不能記錄該根結點的值post
後序遍歷算法的思路this
若是咱們找的到左結點就一直找,找不到的話,咱們拿到棧頂結點,看棧頂的右結點是否找的到,找的到的話,那就指向右結點,而後繼續按照後序遍歷的模式,若是棧頂的右結點找不到,那麼咱們就記錄棧頂的結點,並彈出棧頂結點,而後咱們將新結點令爲空,這樣下次會重新棧頂結點往右邊找。這樣這個算法好像就完了是否是?不是,這裏有個很大的漏洞!我舉個例子說明:假如咱們一直找左結點找到了 a 結點,而後 a 結點的左結點爲 null,a 結點的右結點是 b 結點,b 結點的左結點爲 null,b 結點的右結點也爲 null,當咱們找到 a 結點時候,按照上面的算法思路,a->left 爲空,可是棧頂結點 a 的右結點 b 不爲空,因此新結點是 b 併入棧,b 的左結點爲空,而後棧頂結點 b 的右結點爲空,b 又變成了 a 結點,發現了嗎?這不返回的了嗎?這不從 a 到 b,b 又到 a,這不沒完沒了了?因此算法漏洞是咱們少加了一個東西,咱們應該在當咱們右結點找不到時,須要記錄棧頂的結點,而且當下一次循環訪問右結點時,右結點不只不爲空,並且右結點不能是上次循環中彈出的那個結點!code
Java 實現// 結點 class Node { int data; Node left = null; Node right = null; } // 二叉樹 public class BinaryTree { // 根結點 private Node root; // 輸入的數組 private int[] arr_in; // 輸出的數組 private int[] arr_out; // 記錄數組下標 private static int index; // 初始化 public BinaryTree(int[] arr) { root = new Node(); this.arr_in = arr; arr_out = new int[arr.length]; index = 0; } // 先序遍歷二叉樹(非遞歸)根→左→右 public int[] preorderTraversal(Node r) { Stack stack = new Stack(); Node node = r; while (!stack.empty() || node != null) { if (node != null) { stack.push(node); // 根 arr_out[index++] = node.data; // 左 node = node.left; } else { Node top = stack.pop(); // 右 node = top.right; } } index = 0; return arr_out; } // 中序遍歷二叉樹(非遞歸)左→根→右 public int[] inorderTraversal(Node r) { Stack stack = new Stack(); Node node = r; while (!stack.empty() || node != null) { if (node != null) { stack.push(node); // 左 node = node.left; } else { Node top = stack.pop(); // 根 arr_out[index++] = top.data; // 右 node = top.right; } } index = 0; return arr_out; } // 後序遍歷二叉樹(非遞歸)左→右→根 public int[] postorderTraversal(Node r) { Stack stack = new Stack(); Node node = r; Node top = null; while (!stack.empty() || node != null) { if (node != null) { stack.push(node); // 左 node = node.left; } else { if (stack.peek().right != null && stack.peek().right != top) // 右 node = stack.peek().right; else { top = stack.pop(); // 根 arr_out[index++] = top.data; node = null; } } } index = 0; return arr_out; } }