演算法 - 二叉樹的10種遍歷方法,你都會了么?( 二 )

在上一篇文章(二叉樹的遍歷( 一 ))中,我們通過思考函數入棧出棧的過程,理解了遞歸遍歷二叉樹的原理,下面我們來理解非遞歸的寫法。

在遞歸遍歷中,我們發現,程序首先把根節點的左子孫依次入棧,所以我們可以有如下代碼:

public void traverse1(TreeNode root){ if(root == null) return; Stack<TreeNode> stack = new Stack<>(); TreeNode nodeee = root; while(node != null){ //visit 先序 stack.push(node); node = node.left; }

然後,如果棧不為空,依次出棧,看其是否有右子節點,如果有右節點,執行上一步操作:

while (stack.size() > 0) { node = stack.pop(); //visit 中序 if (node.right != null) { node = node.right; while (node != null) { stack.push(node); node = node.left; } } }}

那麼,先序遍歷的visit點,就在stack.push()之前;中序遍歷的visit點,就在stack.pop() 之後。

上面這兩段代碼可以優化為下面一個循環:

public static void traverse2(TreeNode root){ if(root == null) return; Stack<TreeNode> stack = new Stack<>(); TreeNode node = root; while(!(node==null && stack.empty())){ while(node != null){ //visit 先序 stack.push(node); node = node.left; } node = stack.pop(); //visit 中序 node = node.right; } }

這樣就實現了非遞歸的先序遍歷和中序遍歷。


還有另一種方式實現非遞歸的先序遍歷,用了深度優先搜索(DFS)的思想,其實,二叉樹的先序遍歷,中序遍歷,後序遍歷,都是深度優先搜索。

深度優先搜索:

首先,把根節點入棧。 ①

如果棧不為空,彈出棧頂元素。 ②

把該元素的子節點入棧。 ③

循環②,③,直到棧為空。④

那麼,我們只要保證右節點先於左入棧,就能實現」根->左->右「的先序遍歷。

代碼如下:

public static void traverse3(TreeNode root){ Stack<TreeNode> stack = new Stack<>(); stack.push(root); while(!stack.empty()){ TreeNode node = stack.pop(); //visit 先序 if(node.right != null){ stack.push(node.right); } if(node.left != null) { stack.push(node.left); } } }

從這個思想出發,我們可以很簡單的實現另一種遍歷:」根->右->左「遍歷。

雖然這種遍歷沒有名字,但是他是後序遍歷的反序。所以我們可以利用兩個棧,利用棧的LIFO特點,來實現後續遍歷。

代碼如下:

public static void traverse4(TreeNode root){ Stack<TreeNode> stack = new Stack<>(); Stack<TreeNode> result = new Stack<>(); stack.push(root); while(!stack.empty()){ TreeNode node = stack.pop(); //visit 根->右->左 result.push(node); if(node.left != null) { stack.push(node.left); } if(node.right != null){ stack.push(node.right); } } while(!result.empty()){ node = result.pop(); //visit 後序 } }

那麼,怎麼用一個棧來實現後序遍歷呢?

下面的文章會講到。

為什麼我們模擬程序棧的時候,沒有後續遍歷的visit點呢?

這主要是因為後序遍歷是非尾遞歸的,深層的原因,之後可能會講到。


咳咳,歡迎關注,歡迎點贊,歡迎評論哦~~


推薦閱讀:

TAG:演算法 | 二叉樹 |