天天看點

[LeetCode] #94 二叉樹的中序周遊

[LeetCode] #94 二叉樹的中序周遊

給定一個二叉樹的根節點

root

,傳回它的 中序 周遊。
[LeetCode] #94 二叉樹的中序周遊

輸入:root = [1,null,2,3]

輸出:[1,3,2]

二叉樹周遊,最經典的方法肯定是遞歸

遞歸有三部曲

(1)找整個遞歸的終止條件:遞歸應該在什麼時候結束? 節點為null

(2)找傳回值:應該給上一級傳回什麼資訊?本節點的中序周遊List

(3)本級遞歸應該做什麼:在這一級遞歸中,應該完成什麼任務?本節點List追加左子樹List,追加自己的val,追加右子樹List

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {      
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) return res;
        res.addAll(inorderTraversal(root.left));
        res.add(root.val);
        res.addAll(inorderTraversal(root.right));
        return res;
    }
}      

遞歸也可以用疊代的方式實作,兩種方式是等價的,差別在于遞歸的時候隐式地維護了一個棧,而我們在疊代的時候需要顯式地将這個棧模拟出來,其他都相同。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stk = new LinkedList<TreeNode>();
        while (root != null || !stk.isEmpty()) {
            while (root != null) {
                stk.push(root);
                root = root.left;
            }
            root = stk.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }
}      

Morris中序周遊,是另一種周遊二叉樹的方法,它能将非遞歸的中序周遊空間複雜度降為 O(1)。

[LeetCode] #94 二叉樹的中序周遊
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        TreeNode predecessor = null;

        while (root != null) {
            if (root.left != null) {
                // predecessor 節點就是目前 root 節點向左走一步,然後一直向右走至無法走為止
                predecessor = root.left;
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }
                
                // 讓 predecessor 的右指針指向 root,繼續周遊左子樹
                if (predecessor.right == null) {
                    predecessor.right = root;
                    root = root.left;
                }
                // 說明左子樹已經通路完了,我們需要斷開連結
                else {
                    res.add(root.val);
                    predecessor.right = null;
                    root = root.right;
                }
            }
            // 如果沒有左孩子,則直接通路右孩子
            else {
                res.add(root.val);
                root = root.right;
            }
        }
        return res;
    }
}      

知識點: