1. 程式人生 > >前,中,後序遍歷二叉樹 (遞迴 && 非遞迴的棧 && 非遞迴非棧的線索二叉樹)

前,中,後序遍歷二叉樹 (遞迴 && 非遞迴的棧 && 非遞迴非棧的線索二叉樹)

先簡單介紹下線索二叉樹,線索二叉樹不需要額外的空間便可以O(n)遍歷二叉樹,它充分利用了節點的空指標,若當前結點的左孩子不為空則其左指標指向左孩子結點,否則指向當前節點的前驅;若當前結點的右孩子不為空則其右指標指向右孩子結點,否則指向當前節點的後繼。具體看程式碼實現

前序遍歷二叉樹:

順序:中左右

遞迴:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public void DFS(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        list.add(root.val);
        DFS(root.left, list);
        DFS(root.right, list);
    }
    
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        DFS(root, list);
        return list;
    }
}

非遞迴:先不斷往左遍歷並記錄結點值,然後不斷彈棧向右,這時候向右是因為以當前右結點父親為子樹根的子樹的子樹根和其左子樹已經遍歷完成。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public List<Integer> preorderTraversal(TreeNode root) {
        Stack<TreeNode> stk = new Stack<>();
        List<Integer> list = new ArrayList<>();
        while(!stk.empty() || root != null) {
            while (root != null) {
                stk.push(root);
                list.add(root.val);
                root = root.left;
            }
            if (!stk.empty()) {
                root = stk.peek();
                stk.pop();
                root = root.right;
            }
        }
        return list;
    }
}

非遞迴非棧:找最右孩子是因為在前序遍歷中右子樹最後訪問,通過pre.right回到cur的條件其實蘊含以cur結點左孩子為根的子樹全部遍歷完成

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root, pre = null;
        while(cur != null) {
            if (cur.left == null) {
                list.add(cur.val);
                cur = cur.right;
            }
            else {
                pre = cur.left;
                //找cur的前驅結點
                while(pre.right != null && pre.right != cur) {
                    pre = pre.right;
                }
                //將cur結點置為其前驅結點的右孩子
                if (pre.right == null) {
                    pre.right = cur;
                    list.add(cur.val);
                    cur = cur.left;
                }
                //恢復二叉樹
                else {
                    pre.right = null;
                    cur = cur.right;
                }
            }
        }
        return list;
    }
}


中序遍歷二叉樹:

順序:左中右

遞迴:

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

非遞迴(棧):和前序遍歷不同的地方主要是記錄結點值要在已經遍歷到最左時記錄,因為中序的順序是左中右

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stk = new Stack<>();
        List<Integer> list = new ArrayList<>();
        while (root != null || !stk.empty()) {
            while (root != null) {
                stk.push(root);
                root = root.left;
            }
            if (!stk.empty()) {
                root = stk.peek();
                stk.pop();
                list.add(root.val);
                root = root.right;
            }
        }
        return list;
    }
}

非遞迴非棧:可以發現同前序的區別僅在add的時刻不同,因為中序需要先訪問最左結點,與前序相同的是右子樹最後訪問

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root, pre = null;
        while(cur != null) {
            if (cur.left == null) {
                list.add(cur.val);
                cur = cur.right;
            }
            else {
                pre = cur.left; 
                //找cur的前驅結點
                while(pre.right != null && pre.right != cur) {
                    pre = pre.right;
                }
                //將cur結點置為其前驅結點的右孩子
                if (pre.right == null) {
                    pre.right = cur;
                    cur = cur.left;
                }
                //恢復二叉樹
                else {
                    list.add(cur.val);
                    cur = cur.right;
                    pre.right = null;
                }
            }
        }
        return list;
    }
}

後序遍歷二叉樹:

順序:左右中

遞迴:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    void DFS(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        DFS(root.left, list);
        DFS(root.right, list);
        list.add(root.val);
    }
    
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        DFS(root, list);
        return list;
    }
}

非遞迴:和上面不同,後續遍歷的做法是先將根壓棧然後壓右節點最後壓左結點,當全部壓棧完成後,從棧中取出值記錄即可,注意非葉子結點需要根據其左右孩子是否訪問來確定是否記錄

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    public List<Integer> postorderTraversal(TreeNode root) {
        Stack<TreeNode> stk = new Stack<>();
        List<Integer> list = new ArrayList<>();
        TreeNode pre = root;
        if (root != null) {
            stk.push(root); 
        }
        while (!stk.empty()) {
            root = stk.peek();
            if (root.left == null && root.right == null) {
                list.add(root.val);
                pre = root;
                stk.pop();
                continue;
            }
            if (pre != null && (root.left == pre || root.right == pre)) {
                list.add(root.val);
                pre = root;
                stk.pop();
                continue;
            }
            if (root.right != null) {
                stk.push(root.right);
            }
            if (root.left != null) {
                stk.push(root.left);
            }
        }
        return list;
    }
}

非遞迴非棧:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    
    void reverse(TreeNode from, TreeNode to) {
        if (from == to) {
            return;
        }
        TreeNode x = from, y = from.right, z;  
        while(x != to) {  
            z = y.right;  
            y.right = x;  
            x = y;  
            y = z;  
        }    
    }
    
    void getAns(TreeNode from, TreeNode to, List<Integer> list) {
        reverse(from, to);
        TreeNode p = to;
        while(true) {
            list.add(p.val);
            if (p == from) {
                break;
            }
            p = p.right;
        }
        reverse(to, from);
    }
    
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode help = new TreeNode(-1);
        help.left = root;
        TreeNode cur = help, pre = null;
        while(cur != null) {
            if (cur.left == null) {
                cur = cur.right;
            }
            else {
                pre = cur.left;
                //找cur的前驅結點
                while(pre.right != null && pre.right != cur) {
                    pre = pre.right;
                }
                //將cur結點置為其前驅結點的右孩子
                if (pre.right == null) {
                    pre.right = cur;
                    cur = cur.left;
                }
                //恢復二叉樹
                else {
                    getAns(cur.left, pre, list);
                    pre.right = null;
                    cur = cur.right;
                }
            }
        }
        help.left = null;
        return list;
    }
}