二叉樹的非遞歸遍歷
二叉樹的遍歷如果使用遞歸調用基本沒什麼問題,這裏主要是講如何使用非遞歸方法實現二叉樹的遍歷。
由於遞歸調用程序實際上使用了棧來保存方法中的變量值,在非遞歸遍歷的方法中我們需要基於棧的方法。先來看看這個方法
04 | /// <param name="root"></param> |
05 | static void InOrderTraverse(BinaryTreeNode root) |
07 | BinaryTreeNode temp = root; |
08 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
10 | while (stack.Count > 0) |
22 | Console.WriteLine(temp.data); |
節點temp在這裏是起一個標識的作用,首先沿根節點往左下方進行查找,將存在的節點壓入棧,裏面的那個while循環結束後棧的最頂端一定是一個null,所以棧pop一下,然後這時進行讀取操作,讀取後壓入讀取節點的右子節點,進入下一個while循環,temp指向右子節點。
在這裏使用棧能保證左邊子節點訪問後找到父節點,父節點訪問後也彈出棧,將右子節點壓入。這裏右子節點的壓入和前面一部分是對應的,保證stack.Pop()這句語句的正確性。如果我們不想在棧中壓入多餘的那個null這時該怎麼辦呢?將程序改成這樣
04 | /// <param name="root"></param> |
05 | static void InOrderTraverse2(BinaryTreeNode root) |
07 | BinaryTreeNode temp = root.left; |
08 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
10 | while (stack.Count > 0 || temp != null ) |
18 | Console.WriteLine(temp.data); |
只有確定是非null纔將節點壓入棧,但是這裏會有一個問題,當temp指向根節點的右節點的時候,棧是空的,我們需要在while循環處多加一個判斷,如果temp是null證明右節點不存在,循環結束。
到這裏,程序基本上已經比較完美了,不過我還是要在這裏折騰一下。
while循環中的while循環的條件是temp是否爲null,所以,我可以用一個if/else來換一下
01 | static void InOrderTraverse3(BinaryTreeNode root) |
03 | BinaryTreeNode temp = root.left; |
04 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
06 | while (stack.Count > 0 || temp != null ) |
16 | Console.WriteLine(temp.data); |
呵呵,有意思吧。編程真奇妙~
上面三個都是二叉樹的非遞歸中序遍歷方法,非遞歸先序遍歷和中序差不多,開始從上往下把節點入棧的時候對節點進行操作就行了,比如第二個的中序遍歷改成先序遍歷就是
04 | /// <param name="root"></param> |
05 | static void PreOrderTraverse(BinaryTreeNode root) |
07 | BinaryTreeNode temp = root.left; |
08 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
09 | Console.WriteLine(root.data); |
11 | while (stack.Count > 0 || temp != null ) |
15 | Console.WriteLine(temp.data); |
其他的幾種對着中序改一下就行了
下面來講一講後序遍歷,後序遍歷由於遍歷父節點是在遍歷子節點之後,而且左節點和右節點遍歷後的行爲不一樣,所以需要用變量來記錄前一次訪問的節點,根據前一次節點和現在的節點的關係來確定具體執行什麼操作
01 | static void PostOrderTraversa1(BinaryTreeNode root) |
03 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
05 | BinaryTreeNode prev = null ; |
06 | BinaryTreeNode curr = null ; |
07 | while (stack.Count > 0) |
10 | if (prev == null || prev.left == curr || prev.right == curr) |
12 | if (curr.left != null ) |
14 | stack.Push(curr.left); |
16 | else if (curr.right != null ) |
18 | stack.Push(curr.right); |
22 | Console.WriteLine(curr.data); |
26 | else if (curr.left == prev) |
28 | if (curr.right != null ) |
30 | stack.Push(curr.right); |
34 | Console.WriteLine(curr.data); |
38 | else if (curr.right == prev) |
40 | Console.WriteLine(curr.data); |
01 | static void PostOrderTraversa2(BinaryTreeNode root) |
03 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
05 | BinaryTreeNode prev = null ; |
06 | BinaryTreeNode curr = null ; |
07 | while (stack.Count > 0) |
10 | if (prev == null || prev.left == curr || prev.right == curr) |
12 | if (curr.left != null ) |
13 | stack.Push(curr.left); |
14 | else if (curr.right != null ) |
15 | stack.Push(curr.right); |
17 | else if (curr.left == prev) |
19 | if (curr.right != null ) |
20 | stack.Push(curr.right); |
24 | Console.WriteLine(curr.data); |
好了,最後來一個壓軸的吧。老實說我開始想過這麼搞,但是沒有想清楚就否定了,後來在網上看到別人這麼寫纔看懂。
04 | /// <param name="root"></param> |
05 | static void PostOrderTraversa3(BinaryTreeNode root) |
07 | Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>(); |
08 | Stack<BinaryTreeNode> output = new Stack<BinaryTreeNode>(); |
10 | BinaryTreeNode curr = null ; |
11 | while (stack.Count > 0) |
15 | if (curr.left != null ) |
16 | stack.Push(curr.left); |
17 | if (curr.right != null ) |
18 | stack.Push(curr.right); |
20 | while (output.Count > 0) |
22 | Console.WriteLine(output.Peek().data); |