1. 程式人生 > >非遞迴實現樹的前中後序遍歷總結 --- Java語言實現

非遞迴實現樹的前中後序遍歷總結 --- Java語言實現

前言

三種遍歷的遞迴寫法都很好寫,所以總結一下非遞迴寫法。 先貼一張圖複習一下三種遍歷方式就進入正文啦~ 在這裡插入圖片描述 【注:本文所有程式碼實現中樹的結點定義如下:

public class Node {
    int val;
    Node left;
    Node right;
    Node parent;

    Node() {}

    Node(int val) {
        this.val = val;
    }
}

1.前序遍歷

實現思路:

前序遍歷的順序是:根結點 -> 左孩子 -> 右孩子 藉助一個棧結構先將根結點壓入棧,然後迴圈每次取出棧頂元素,直到棧為空。如果當前結點有右孩子就先將右孩子壓入棧中,如果當前結點有左孩子就將左孩子壓入棧中,這裡注意順序,因為棧的結構是先進後出的,為了保證先遍歷到左孩子就應該後壓左孩子~

程式碼實現:

【程式碼使用直接輸出的方式列印中序遍歷結果,也可以放入一個list中儲存~】

public void preOrder(Node head) {
        System.out.println("pre-order:");
        if(head != null) {
            Stack<Node> stack = new Stack<>();
            stack.push(head);
            while(!stack.isEmpty()) {
                head = stack.
pop(); System.out.print(head.val + " "); if (head.right != null) stack.push(head.right); if (head.left != null) stack.push(head.left); } } }

2.中序遍歷

實現思路:

中序遍歷的順序:左孩子 -> 根結點 -> 右孩子 藉助棧結構,將當前結點的左孩子依次入棧直到沒有左孩子了就彈出棧頂元素並列印,如果彈出的結點有右孩子就將右孩子的左邊界依次入棧,迴圈… 比如文章開始的那個圖中,先將A,B,D依次入棧,此時棧頂元素是D,彈出並列印,D結點有右孩子,將D的右孩子的左邊界依次入棧,壓入結點G,結點G沒有左孩子所以彈出並列印G,此時棧頂元素是B,迴圈…直到棧中為空且當前結點為空時遍歷結束。

程式碼實現:

public static void inOrderTraverse(Node head) {
        System.out.println("in-order:");
        if(head != null) {
            Stack<Node> stack = new Stack<>();
            while(!stack.isEmpty() || head != null) {
                if(head != null) {
                    // 當前節點不為空, 將自己壓進棧並將自己的左孩子作為當前節點(壓入左邊界)
                    stack.push(head);
                    head = head.left;
                }else {
                    // 當前節點為空(沒有左孩子了), 將棧頂元素彈出作為當前節點, 並將當前節點的右孩子壓進棧
                    head = stack.pop();
                    System.out.print(head.val + " ");
                    head = head.right;
                }
            }
        }
    }

3.後序遍歷

實現思路:

後序遍歷的順序:左孩子 -> 右孩子 -> 根結點 仔細想一下,列印每個結點需要訪問根結點三次,先從根結點開始找到左孩子,返回再找到右孩子,再返回到根結點,需要訪問根結點三次,直接按照當前順序進行遍歷不知如何下手,那麼我們可以換一個角度去考慮。 棧結構是先進後出的,那我們不妨藉助一個棧先儲存 根結點 -> 右孩子 -> 左孩子 的結果,再將其依次彈出就是後序遍歷的順序了。 那實現 根結點 -> 右孩子 -> 左孩子 的方法就很簡單了,回憶一下剛才說的前序遍歷的方式,前序遍歷是先要左孩子,就後壓入左孩子,反之,將前序遍歷的邏輯改寫為:彈出每個棧頂結點作為當前結點並存儲到一個輔助棧中,如果當前結點有左孩子就先壓入左孩子,如果有右孩子就後壓入右孩子,每次取棧頂彈出到輔助棧中。最後將得到的輔助棧中元素依次彈出得到的就是後序遍歷的結果。

程式碼實現:

public static void posOrderTraverse(Node head) {
        System.out.println("pos-order");
        if(head != null) {
            Stack<Node> stack1 = new Stack<>();
            Stack<Node> stack2 = new Stack<>();     // 輔助棧,儲存 根 -> 右 -> 左 的結果
            stack1.push(head);
            while(!stack1.isEmpty()) {
                head = stack1.pop();
                stack2.push(head);
                // 有左孩子就先壓入左孩子
                if(head.left != null)
                    stack1.push(head.left);
                // 有右孩子就後壓入右孩子
                if(head.right != null)
                    stack1.push(head.right);
            }
            // 逆序列印 根 -> 右 -> 左 的結果,就是後序遍歷的結果
            while(!stack2.isEmpty())
                System.out.print(stack2.pop().val + " ");
        }
    }