二叉樹中序遍歷的常見方法

概述

中序遍歷,是二叉樹遍歷的一種。在二叉樹中,中序遍歷首先遍歷左子樹,然後遍歷根節點,最後遍歷右子樹。

遞歸遍歷法

public List<Integer> getLDRMethod1(TreeNode treeNode) {
    List<Integer> result = new ArrayList<>();
    if (treeNode != null) {
        result.addAll(getLDRMethod1(treeNode.left));
        result.add(treeNode.val);
        result.addAll(getLDRMethod1(treeNode.right));
    }
    return result;
}

遞歸遍歷法就像概述中二叉樹定義的一種實現,首先遍歷左子樹,然後遍歷根節點,最後遍歷右子樹。這種方法一般用來求中序遍歷結果,因爲遞歸遍歷過程中,方法參數過多本身也是一種資源浪費,而只傳遞節點的話又不好計算各種累加問題,因此該方法在算法中,能用到場景較少。如果一定要用,也可以通過全局變量的方式來完成。

迭代法

public List<Integer> getLDRMethod(TreeNode treeNode) {
    List<Integer> result = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    while (!stack.isEmpty() || treeNode != null) {
        while (treeNode != null) {
            stack.add(treeNode);
            treeNode = treeNode.left;
        }
        treeNode = stack.pop();
        result.add(treeNode.val);
        treeNode = treeNode.right;
    }
    return result;
}

該迭代方法需要使用棧來保存還未遍歷的節點,遍歷思路也比較簡單,一直向左遍歷,模擬先遍歷左子樹這一步。如果左爲空,就記錄該節點值,模擬遍歷根節點這一步,然後向右遍歷,模擬遍歷右子樹這一步,一直循環到遍歷完所有節點。該方法在算法中經常使用,無論是計算累加問題,還是判斷問題,相比遞歸方法更高效和容易理解,也不需要使用全局變量,唯一的缺點就是需要引入新的棧空間,記錄還沒有遍歷的節點。

Morris中序遍歷

public List<Integer> getLDRMethod3(TreeNode treeNode) {
    List<Integer> result = new ArrayList<>();
    TreeNode predecessor = null;
    while (treeNode != null) {
        if (treeNode.left != null) {
            predecessor = treeNode.left;
            while (predecessor.right != null && predecessor.right != treeNode) {
                predecessor = predecessor.right;
            }
            if (predecessor.right == null) {
                predecessor.right = treeNode;
                treeNode = treeNode.left;
            } else {
                predecessor.right = null;
                result.add(treeNode.val);
                treeNode = treeNode.right;
            }
        } else {
            result.add(treeNode.val);
            treeNode = treeNode.right;
        }
    }
    return result;
}

看名字就知道該方法是一個比較難理解的方法。難理解的方法又是相對比較高效的方法:Morris遍歷是一個不需要使用棧,僅僅使用一個局部變量就可以完成中序遍歷的高效方法。在講解代碼之前,我先簡單介紹Morris中序遍歷的大致解決理念:

  1. Morris方法使每個節點的右指針都指向中序遍歷的下一個節點
  2. 有了理念1,我們也可以說,每次指向right的時候,就是遍歷的時候

那麼它具體是怎麼做的呢:

  1. 假設根節點A的左子樹B沒有右子樹,那麼B的右子樹就是A。
  2. 假設根節點A的左子樹B有右子樹,那麼就一直向右遍歷,直到它沒有右子樹,設置它的右子樹爲A

上述方案確定A的左子樹中序遍歷最後一個節點 的 right 指向它自己,也就是保證左子樹遍歷完成後,遍歷根節點。左子樹遍歷完有兩種情況:

  1. 左子樹爲空,說明這個節點沒有左子樹,當然也可以理解爲左子樹已經遍歷完了
  2. 左子樹的右節點遞歸最終指向自己,說明它的左子樹已經遍歷完成了,同時刪掉這個右指針,維護樹結構。

帶着這些結論我們來解釋上述方法的工作原理:

  1. 如果左子樹爲空,直接遍歷該節點,進入右子樹。
  2. 如果左子樹不爲空,取它的左子樹節點,遞歸該節點的右指針,直到爲空
  3. 設置該節點的右指針指向父節點,表示父節點的左子樹遍歷完成後,最後一個幾點的right指向它。
  4. 如果該節點的右指針已經指向父節點了,說明該父節點的左子樹已經遍歷完了,刪除這個新加的right標記,遍歷該節點,進入右子樹
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章