java數據結構與算法總結(十三)--二叉樹(遍歷方法、遞歸實現)

二叉樹的遍歷


二叉樹的遍歷是指按照某種順序訪問二叉樹中的每個結點,使每個結點被訪問一次且僅被訪問一次。通過一次完整的遍歷,可使二叉樹中的結點信息由非線性排列變成某種意義上的線性序列。也就是說,遍歷操作是非線性結構線性化。

由二叉樹的定義可知,一棵二叉樹由根結點、左子樹和右子樹三部分組成。因此,只需要依次遍歷這三個部分,就可以遍歷整個二叉樹。若以D、L、R分別表示訪問根結點、遍歷根結點的左子樹和遍歷根結點的右子樹,則二叉樹的遍歷方式有6種,它們的含義如下表所示:

  先左後右 先右後左
先序遍歷 DLR DRL
中序遍歷 LDR RDL
後序遍歷 LRD RLD


如果限定先左後右,則只有前三種方式。但這只是由根結點到左右子樹結點的方式來遍歷,其實還有一種遍歷方式,層次遍歷。下面就來討論這四種遍歷方式。

二叉樹的遍歷方式


先序遍歷


先序遍歷的遞歸過程爲:若二叉樹爲空,遍歷結束;否則,訪問根結點,先序遍歷根結點的左子樹,先序遍歷根結點的右子樹

對於上圖的先序遍歷,先遍歷根結點A,再遍歷A的左子樹。而要遍歷A的左子樹,先遍歷A的左子樹的根結點B,再遍歷它的左子樹。如此循環。最終得到的結點序列:ABDGHCEIF。

    public void preorder(Node<E> p) {
        if (isEmpty()) {
            System.out.println("Tree is empty");
            return;
        }
        if (p != null) {
            System.out.print(p.getData() + " ");
            preorder(p.getLchild());
            preorder(p.getRchild());
        }
    }


中序遍歷


中序遍歷的遞歸過程爲:若二叉樹爲空,遍歷結束;否則,中序遍歷根結點的左子樹,訪問根結點,中序遍歷根結點的右子樹。

對於上圖的中序遍歷,要遍歷A根點,先遍歷它的左子樹。要遍歷A的左子樹,就要遍歷它左子樹的左子樹。也就是說,最開始遍歷的是最左邊的結點G。然後遍歷根結點,再右子樹。如此循環。最終得到的結點序列:GDHBAEICF。

    public void inorder(Node<E> p) {
        if (isEmpty()) {
            System.out.println("Tree is empty");
            return;
        }
        if (p != null) {
            inorder(p.getLchild());
            System.out.print(p.getData() + " ");
            inorder(p.getRchild());
        }
    }


後序遍歷


後序遍歷的遞歸過程爲:若二叉樹爲空,遍歷結束;否則,後序遍歷根結點的左子樹,後序遍歷根結點的右子樹,訪問根結點。

對於上圖的後序遍歷,要遍歷A根點,先遍歷它的左子樹。要遍歷A的左子樹,就要遍歷它左子樹的左子樹。也就是說,最開始遍歷的是還是最左邊的結點G。然後遍歷右子樹,再根結點。如此循環。最終得到的結點序列:GHDBIEFCA。

    

public void postorder(Node<E> p) {
        if (isEmpty()) {
            System.out.println("Tree is empty");
            return;
        }
        if (p != null) {
            postorder(p.getLchild());
            postorder(p.getRchild());
            System.out.print(p.getData() + " ");
        }
    }


層次遍歷


所謂層次遍歷,就是從二叉樹的第一層(根結點)開始,從上至下逐層遍歷,在同一層,則按照從左至右的順序對每個結點逐個訪問。

層次遍歷的基本思想是:由於層次遍歷結點的順序是先遇到的結點先訪問,與隊列的操作順序相同。所以,在進行層次遍歷時,設置一個隊列,將根結點引用入隊,當隊列非空時,循環執行以下三步:

  1. 從隊列中取出一個結點的引用,並訪問該節點;
  2. 若該結點的左子樹非空,將該結點的左子樹引用入隊;
  3. 若該結點的右子樹非空,將該結點的右子樹引用入隊。
public void levelOrder(Node<E> root) {
        // 根結點爲空
        if (root == null) {
            return;
        }
        // 設置一個隊列保存層序遍歷的結點
        Queue<Node<E>> q = new LinkedList<Node<E>>();
        // 根結點入隊
        q.add(root);
        // 隊列非空,結點沒有處理完
        while (!q.isEmpty()) {
            // 結點出隊
            Node<E> tmp = q.poll();
            // 處理當前結點
            System.out.print(tmp.getData() + " ");
            // 將當前結點的左孩子結點入隊
            if (tmp.getLchild() != null) {
                q.add(tmp.getLchild());
            }
            if (tmp.getRchild() != null) {
                // 將當前結點的右孩子結點入隊
                q.add(tmp.getRchild());
            }
        }
    }


總結與分析


二叉樹的四種遍歷方式,先序遍歷、中序遍歷、後序遍歷、層次遍歷,除了層次遍歷使用隊列的方式之外,其他三種都是採用的遞歸的方式實現的,而遞歸是可以用棧的方式來實現的。也就是說二叉樹的遞歸方式說到底也就是棧和隊列的兩種實現方式。這和圖的遍歷方式(深度優先、廣度優先)的思想是一致的。
————————————————
版權聲明:本文爲CSDN博主「Yngz_Miao」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_38410730/article/details/79587728

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章