刷題筆記:樹的前序、中序、後序遍歷

起個名字好難__發表於2020-10-31

在此之前,我們定義二叉樹的資料結構如下:


class TreeNode//節點結構
{
	int value;
	List<TreeNode> children=new ArrayList<>();
	TreeNode left;
	TreeNode right;
	
	TreeNode(int value)
	{
		this.value = value;
	}
}


//主函式
public class main {

    public static List<Integer> res=new ArrayList<>();

    public static void main(String[] args) {
    //以下為前序後續遍歷N叉樹建樹的過程
        TreeNode[] node = new TreeNode[10];//以陣列形式生成一棵完全二叉樹
        for(int i = 0; i < 10; i++)
        {
            node[i] = new TreeNode(i);
        }

        //建樹
        for(int i = 0; i < 10; i++)
        {
            if(i*2+1 < 10)
                node[i].children.add(node[i*2+1]);
            if(i*2+2 < 10)
                node[i].children.add(node[i*2+2]);
        }

//		  註釋部分適用於中序遍歷二叉樹建立樹結構
//        TreeNode[] node = new TreeNode[10];
//        for(int i = 0; i < 10; i++)
//        {
//            node[i] = new TreeNode(i);
//        }
//        for(int i = 0; i < 10; i++)
//        {
//            if(i*2+1 < 10)
//                node[i].left = node[i*2+1];
//            if(i*2+2 < 10)
//                node[i].right = node[i*2+2];
//        }



//        System.out.println("前序遞迴");
//        PreOrder.preOrderRecursion(node[0],res);
//        System.out.println(res.toString());
//
//        System.out.println("前序非遞迴");
//        List<Integer> resultPre=PreOrder.preOrder(node[0]);
//        System.out.println(resultPre.toString());

//        System.out.println("中序遞迴");
//        MidOrder.midOrderRecursion(node[0],res);
//        System.out.println(res.toString());
//
//        System.out.println("中序非遞迴");
//        List<Integer> resultMid=MidOrder.midOrder(node[0]);
//        System.out.println(resultMid.toString());


        System.out.println("後序遞迴");
        LateOrder.postOrderRe(node[0],res);
        System.out.println(res.toString());

        System.out.println("後序非遞迴");
        List<Integer> resultMid=LateOrder.postOrder(node[0]);
        System.out.println(resultMid.toString());

    }
}

前序遍歷

前序遍歷又稱先根遍歷,遍歷順序是根->左->右。遍歷步驟是:

若 二叉樹為空則結束返回,否則:
(1)訪問根結點。
(2)前序遍歷左子樹 。
(3)前序遍歷右子樹 。
在這裡插入圖片描述
上圖的前序遍歷為:ABDECF.

注意:已知後序遍歷和中序遍歷,就能確定前序遍歷。

遞迴

public static void preOrderRecursion(TreeNode treeNode,List<Integer> res){

        List<TreeNode> children=treeNode.children;
        res.add(treeNode.value);
        for(TreeNode node:children){
            if(node!=null){
                preOrderRecursion(node,res);
            }
        }
    }

非遞迴

//非遞迴,需要依靠棧實現
    public static List<Integer> preOrder(TreeNode treeNode){
        Stack<TreeNode> stack=new Stack<>();
        List<Integer> res=new ArrayList<>();
        stack.add(treeNode);
        while (!stack.isEmpty()){
            TreeNode root=stack.pop();
            res.add(root.value);
            Collections.reverse(root.children);
            for(TreeNode child:root.children){
                stack.add(child);
            }
        }
        return res;
    }

中序遍歷

中序遍歷又稱中根遍歷,遍歷順序是左->根->右。
若二叉樹為空則結束返回,
否則:
(1)中序遍歷左子樹
(2)訪問根結點
(3)中序遍歷右子樹

在這裡插入圖片描述
上圖的中序遍歷結果為:DBEAFC

遞迴

public static void midOrderRecursion(TreeNode treeNode, List<Integer> res){

        if (treeNode==null)
            return;
        else{
            midOrderRecursion(treeNode.left, res);
            res.add(treeNode.value);
            midOrderRecursion(treeNode.right, res);
        }
  }

非遞迴

//非遞迴,需要依靠棧實現
    public static List<Integer> midOrder(TreeNode treeNode){
        Stack<TreeNode> stack=new Stack<>();
        List<Integer> res=new ArrayList<>();
        while(treeNode != null || !stack.isEmpty()){
            while (treeNode!=null){
                stack.push(treeNode);
                treeNode = treeNode.left;
            }
            if(!stack.isEmpty()){
                treeNode=stack.pop();
                res.add(treeNode.value);
                treeNode = treeNode.right;
            }
        }
        return res;
    }

後續遍歷

後續遍歷,又稱後根遍歷。即先輸出孩子節點,後輸出跟節點,遍歷順序左->右->根。
若二叉樹為空則結束返回,
否則:
(1)後序遍歷左子樹
(2)後序遍歷右子樹
(3)訪問根結點

遞迴

public static void postOrderRe(TreeNode treeNode, List<Integer> res)
    {//後序遍歷遞迴實現
        if(treeNode == null)
            return;
        else
        {
            List<TreeNode> children=treeNode.children;

            for(TreeNode node:children){
                if(node!=null){
                    postOrderRe(node,res);
                }
            }
            res.add(treeNode.value);
        }
    }

非遞迴

後續遍歷的非遞迴演算法與前序遍歷的非遞迴演算法很像。我們舉個例子:
在後序遍歷中,我們會先遍歷一個節點的所有子節點,再遍歷這個節點本身。例如當前的節點為 u,它的子節點為 v1, v2, v3 時,那麼後序遍歷的結果為 [children of v1], v1, [children of v2], v2, [children of v3], v3, u,其中 [children of vk] 表示以 vk 為根節點的子樹的後序遍歷結果(不包括 vk 本身)。我們將這個結果反轉,可以得到 u, v3, [children of v3]', v2, [children of v2]', v1, [children of v1]',其中 [a]’ 表示 [a] 的反轉。此時我們發現,結果和前序遍歷非常類似,只不過前序遍歷中對子節點的遍歷順序是 v1, v2, v3,而這裡是 v3, v2, v1

因此我們可以使用和 N叉樹的前序遍歷 相同的方法,使用一個棧來得到後序遍歷。我們首先把根節點入棧。當每次我們從棧頂取出一個節點 u 時,就把 u 的所有子節點順序推入棧中。例如 u 的子節點從左到右為 v1, v2, v3,那麼推入棧的順序應當為 v1, v2, v3,這樣就保證了下一個遍歷到的節點(即 u 的第一個子節點 v3)出現在棧頂的位置。在遍歷結束之後,我們把遍歷結果反轉,就可以得到後序遍歷。(以上參考Leetcode題解

public static List<Integer> postOrder(TreeNode treeNode)
    {
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> output = new ArrayList<>();
        if (treeNode == null) {
            return output;
        }

        stack.add(treeNode);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            output.add(node.value);
            for (TreeNode item : node.children) {
                if (item != null) {
                    stack.push(item);
                }
            }
        }

        Collections.reverse(output);

        return output;

    }


相關文章