二叉樹的遞歸與迭代遍歷

本文將針對二叉樹中幾種常見的遍歷方法進行介紹。

遍歷方式

前序遍歷

前序遍歷首先訪問根節點,然後遍歷左子樹,最後遍歷右子樹。

中序遍歷

中序遍歷是先遍歷左子樹,然後訪問根節點,然後遍歷右子樹。

後序遍歷

後序遍歷是先遍歷左子樹,然後遍歷右子樹,最後訪問樹的根節點。

遞歸實現

遞歸實現二叉樹的遍歷是非常簡單的,其核心就是 深度優先搜索(DFS) 算法。

由於比較簡單,三種遍歷方式的實現代碼只是 深度優先搜索 過程中執行順序的區別,故模版如下:

public class Solution {

    // 遞歸遍歷二叉樹
    public List<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        if (root == null) return list;

        dfs(root, list);
        return list;
    }

    private void dfs(TreeNode root, ArrayList<Integer> list) {
        if (root == null) return;

        // 前序遍歷 根 -> 左 -> 右
        list.add(root.val);     // 根
        dfs(root.left, list);   // 左
        dfs(root.right, list);  // 右

        // 中序遍歷 右 -> 根 -> 右
    //    dfs(root.left, list);
    //    list.add(root.val);
    //    dfs(root.right, list);

        // 後序遍歷 左 -> 右 -> 根
    //    dfs(node.left, list);
    //    dfs(node.right, list);
    //    list.add(node.val);
    }
}

迭代實現

前序遍歷

通過迭代對前序遍歷需要一個棧進行輔助,其負責對不同層級父子節點進行迭代存儲。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
      ArrayList<Integer> list = new ArrayList<>();
      Stack<TreeNode> stack = new Stack<>();

      TreeNode curr = root;

      // 1.退出最外層迭代的條件是,指針指向null,且棧爲空
      while (curr != null || !stack.isEmpty()) {
          // 2.內層循環按順序入棧,同時更新當前指針
          // 4.這時候也可能是開始遍歷右節點
          while (curr != null) {
              stack.push(curr);
              list.add(curr.val);
              curr = curr.left;
          }
          // 3.返回父節點,並將指針指向右節點
          curr = stack.pop();
          curr = curr.right;
      }

      return list;
    }
}

中序遍歷

中序遍歷和前序遍歷思想是一致的,區別僅僅在於根節點在左葉子節點添加之後添加:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
      List<Integer> list = new ArrayList<>();
      Stack<TreeNode> stack = new Stack<>();

      TreeNode curr = root;

      // 1.退出最外層迭代的條件是,指針指向null,且棧爲空
      while (!stack.isEmpty() || curr != null) {
          // 2.內層循環按順序入棧,同時更新當前指針
          // 5.這時候也可能是開始遍歷右節點
          while (curr != null) {
              stack.push(curr.left);
              curr = curr.left;
          }
          // 3.返回父節點,並加入數組中
          curr = stack.pop();
          list.add(curr.val);
          // 4.將指針指向右節點
          curr = curr.right;
      }
      return list;
    }
}

後序遍歷

後序遍歷 LeetCode 官方題解給出了一個額外的思路,對於樹的 後序遍歷 而言,其遍歷順序與 廣度優先搜索(BFS) 恰恰是相反的:

如圖所示,BFS的遍歷順序是 1->2->3->4->5,而相同的樹後序遍歷順序則是 4->5->2->3->1

因此,後序遍歷的思路如下:

從根節點開始依次迭代,彈出棧頂元素輸出到輸出列表中,然後依次壓入它的所有孩子節點,按照從上到下、從左至右的順序依次壓入棧中。

因爲深度優先搜索後序遍歷的順序是從下到上、從左至右,所以需要將輸出列表逆序輸出。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> output = new LinkedList<>();
        // 和傳統的bfs不同,這裏並沒有用 Queue
        // 因爲順序是相反的,這裏並不是取第一個元素,而是取棧頂的元素(即同層級節點從右->左遍歷)
        Stack<TreeNode> stack = new Stack<>();

        if (root == null) return output;

        stack.push(root);

        while(!stack.isEmpty()) {
           TreeNode node = stack.pop();
           output.addFirst(node.val);

           if (node.left != null) {
               stack.push(node.left);
           }
           if (node.right != null) {
               stack.push(node.right);
           }
        }
        return output;
    }
}

參考 & 感謝

文章絕大部分內容節選自LeetCode,概述:

  • https://leetcode-cn.com/explore/learn/card/data-structure-binary-tree/2/traverse-a-tree/7/

例題:

  • https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
  • https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
  • https://leetcode-cn.com/problems/binary-tree-postorder-traversal/

關於我

Hello,我是 卻把清梅嗅 ,如果您覺得文章對您有價值,歡迎 ❤️,也歡迎關注我的 博客 或者 GitHub

如果您覺得文章還差了那麼點東西,也請通過關注督促我寫出更好的文章——萬一哪天我進步了呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章