1. 程式人生 > >二叉樹的遍歷(遞迴與非遞迴)

二叉樹的遍歷(遞迴與非遞迴)

本文討論二叉樹的常見遍歷方式的程式碼(Java)實現,包括前序(preorder)、中序(inorder)、後序(postorder)、層序(level order),進一步考慮遞迴和非遞迴的實現方式。

遞迴的實現方法相對簡單,但由於遞迴的執行方式每次都會產生一個新的方法呼叫棧,如果遞迴層級較深,會造成較大的記憶體開銷,相比之下,非遞迴的方式則可以避免這個問題。遞迴遍歷容易實現,非遞迴則沒那麼簡單,非遞迴呼叫本質上是通過維護一個棧,模擬遞迴呼叫的方法呼叫棧的行為。

在此之前,先簡單定義節點的資料結構:

二叉樹節點最多隻有兩個兒子,並儲存一個節點的值,為了實驗的方便,假定它為 int。同時,我們直接使用 Java 的 System.out.print 方法來輸出節點值,以顯示遍歷結果。

1
2
3
4
5
6
7
8
9
public class Node {
      public int value;
      public Node leftNode;
      public Node rightNode;
      public Node(int i) {
          value = i;
      }
  }

前序遍歷

遞迴實現

遞迴實現很簡單,在每次訪問到某個節點時,先輸出節點值,然後再依次遞迴的對左兒子、右兒子呼叫遍歷的方法。程式碼如下

1
2 3 4 5 6 7
public void preOrderTrav(Node n) {
  if (n != null) {
      System.out.print(n.value + " ");
      preOrderTrav(n.leftNode);
      preOrderTrav(n.rightNode);
  }
}

非遞迴調實現

方法 1
1
2
3
4
5
6
7
8
9
10
11
12
public void preOrderTravNoRecur(Node n) {
  Stack
<Node> stack = new Stack<Node>();
stack.add(root); while (!stack.empty()) { Node t = stack.pop(); System.out.print(t.value + " "); if (t.rightNode != null) stack.add(t.rightNode); if (t.leftNode != null) stack.add(t.leftNode); } }
  • 描述

    維護一個棧,將根節點壓入棧中。此後,每次從棧中讀出棧頂的節點,作為對節點的訪問,然後將該節點的兒子節點按照先右後左的順序,壓入棧中,實現遞迴模擬。

  • 分析

    這個棧的遞迴策略不具備很好的擴充套件性,其他的遍歷方式無法使用這種策略。實際上,它並不是對程式呼叫棧的模擬,而是針對先序遍歷的特殊實現:先序遍歷先對當前節點做出訪問後,然後遞迴的呼叫對兒子節點的遍歷,不需要在對兒子節點遍歷結束後再回過頭來處理當前節點。於是模擬的遞迴中也不需要儲存之前的呼叫棧資訊,只需要類似的生成一個未來的兒子節點的訪問計劃即可。

方法 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void preOrderTravNoRecurII(Node n) {
  System.out.println("No Recursive: ");
  Stack<Node> s = new Stack<Node>();
  while (n != null | !s.empty()){
      while