原題連接:java
三道題的解決思路可統一,模板也極其類似,比九章提供的更漂亮。node
好比{1,2,3},當cur位於節點1時,一、2屬於「左」節點,3屬於「右」節點。DFS的非遞歸實現本質上是在協調入棧、出棧和訪問,三種操做的順序。上述統一使得咱們再也不須要關注入棧順序,僅須要關注出棧和訪問(第3點),隨着更詳細的分析,你將更加體會到這種簡化帶來的好處。git
將對節點的訪問定義爲results.add(node.val);
,分析以下:github
先序和中序的狀況是極其類似的。post
使用上述思路,先序和中序的遍歷順序可統一爲:「左」「右」。spa
給咱們的直觀感受是代碼也會比較類似。實際狀況正是如此,先序與中序的區別只在於對「左」節點的訪問上。code
不須要入棧,每次遍歷到「左」節點,當即輸出便可。對象
須要注意的是,遍歷到最左下的節點時,實際上輸出的已經再也不是實際的根節點,而是實際的左節點。這符合先序的定義。遞歸
while (cur != null) {
results.add(cur.val);
stack.push(cur);
cur = cur.left;
}
複製代碼
然後,由於咱們已經訪問過全部「左」節點,如今只須要將這些沒用的節點出棧,而後轉向到「右」節點。因而「右」節點也變成了「左」節點,後續處理同上。get
if (!stack.empty()) {
cur = stack.pop();
// 轉向
cur = cur.right;
}
複製代碼
完整代碼以下:
private List<Integer> dfsPreOrder(TreeNode root) {
ArrayList<Integer> results = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.empty()) {
while (cur != null) {
results.add(cur.val);
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
// 轉向
cur = cur.right;
}
return results;
}
複製代碼
基於對先序的分析,先序與中序的區別只在於對「左」節點的處理上
,咱們調整一行代碼便可完成中序遍歷。
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
results.add(cur.val); // 僅調整該行代碼
// 轉向
cur = cur.right;
複製代碼
注意,咱們在出棧以後才訪問這個節點。由於先序先訪問實際根,後訪問實際左,而中序剛好相反。相同的是,訪問完根+左子樹(先序)或左子樹+根(中序)後,都須要轉向到「右」節點,使「右」節點稱爲新的「左」節點。
完整代碼以下:
private List<Integer> dfsInOrder(TreeNode root) {
List<Integer> results = new ArrayList<>();
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode cur = root;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
results.add(cur.val);
cur = cur.right;
}
return results;
}
複製代碼
後序的狀況略有不一樣,但仍然十分簡潔。
入棧順序不變,咱們只須要考慮第3點的變化(合適時機轉向
)。出棧的對象必定都是「左」節點(「右」節點會在轉向後稱爲「左」節點,而後入棧),也就是實際的左或根;實際的左能夠當作左右子樹都爲null的根,因此咱們只須要分析實際的根。
對於實際的根,須要保證前後訪問了左子樹、右子樹以後,才能訪問根。實際的右節點、左節點、根節點都會成爲「左」節點入棧,因此咱們只須要在出棧以前,將該節點視做實際的根節點,並檢查其右子樹是否已被訪問便可。若是不存在右子樹,或右子樹已被訪問了,那麼能夠訪問根節點,出棧,並不須要轉向;若是尚未訪問,就轉向,使其「右」節點成爲「左」節點,等着它先被訪問以後,再來訪問根節點。
因此,咱們須要增長一個標誌,記錄右子樹的訪問狀況。因爲訪問根節點前,必定先緊挨着訪問了其右子樹,因此咱們只須要一個標誌位。
完整代碼以下:
private List<Integer> dfsPostOrder(TreeNode root) {
List<Integer> results = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode last = null;
while(cur != null || !stack.empty()){
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.peek();
if (cur.right == null || cur.right == last) {
results.add(cur.val);
stack.pop();
// 記錄上一個訪問的節點
// 用於判斷「訪問根節點以前,右子樹是否已訪問過」
last = cur;
// 表示不須要轉向,繼續彈棧
cur = null;
} else {
cur = cur.right;
}
}
return results;
}
複製代碼
思路簡潔萬歲!模板大法萬歲!
消滅人類暴政,世界屬於三體!
本文連接:【刷題】二叉樹非遞歸遍歷
做者:猴子007
出處:monkeysayhi.github.io
本文基於 知識共享署名-相同方式共享 4.0 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。