圖解 二叉樹的四種遍歷


圖解 二叉樹的四種遍歷

 

LeetCode 題目中,二叉樹的遍歷方式是最基本,也是最重要的一類題目,我們將從「前序」、「中序」、「后序」、「層序」四種遍歷方式出發,總結他們的遞歸和迭代解法。

題目說明

這里是 4 道相關題目:

  1. 144.二叉樹的前序遍歷
  2. 94. 二叉樹的中序遍歷
  3. 145. 二叉樹的后序遍歷
  4. 102. 二叉樹的層序遍歷

二叉樹及遍歷方式

要解決這四道題目,最基本的前提是要了解什么是二叉樹,以及二叉樹的遍歷方式。如果你已經有所了解,則可以直接查看下一節的內容。

首先,二叉樹是一種「數據結構」,詳細的介紹可以參考力扣的「探索」卡片來進行學習。簡單來說,就是一個包含節點,以及它的左右孩子的一種數據結構。

如果對每一個節點進行編號,你會用什么方式去遍歷每個節點呢?

如果你按照 根節點 -> 左孩子 -> 右孩子 的方式遍歷,即「先序遍歷」,每次先遍歷根節點,遍歷結果為 1 2 4 5 3 6 7

同理,如果你按照 左孩子 -> 根節點 -> 右孩子 的方式遍歷,即「中序序遍歷」,遍歷結果為 4 2 5 1 6 3 7

如果你按照 左孩子 -> 右孩子 -> 根節點 的方式遍歷,即「后序序遍歷」,遍歷結果為 4 5 2 6 7 3 1

最后,層次遍歷就是按照每一層從左向右的方式進行遍歷,遍歷結果為 1 2 3 4 5 6 7

題目解析

這四道題目描述是相似的,就是給定一個二叉樹,讓我們使用一個數組來返回遍歷結果,首先來看遞歸解法。

遞歸解法

由於層次遍歷的遞歸解法不是主流,因此只介紹前三種的遞歸解法。它們的模板相對比較固定,一般都會新增一個 dfs 函數:

對於前序、中序和后序遍歷,只需將遞歸函數里的 res.append(root.val) 放在不同位置即可,然后調用這個遞歸函數就可以了,代碼完全一樣。

1. 前序遍歷

2. 中序遍歷

3. 后序遍歷

一樣的代碼,稍微調用一下位置就可以,如此固定的套路,使得只掌握遞歸解法並不足以令面試官信服。

因此我們有必要再掌握迭代解法,同時也會加深我們對數據結構的理解。

1. 二叉樹的前序遍歷

LeetCode 題目: 144.二叉樹的前序遍歷

常規解法

我們使用棧來進行迭代,過程如下:

  • 初始化棧,並將根節點入棧;
  • 當棧不為空時:
    • 彈出棧頂元素 node,並將值添加到結果中;
    • 如果 node 的右子樹非空,將右子樹入棧;
    • 如果 node 的左子樹非空,將左子樹入棧;

由於棧是“先進后出”的順序,所以入棧時先將右子樹入棧,這樣使得前序遍歷結果為 “根->左->右”的順序。

參考代碼如下:

模板解法

當然,你也可以直接啟動“僵屍”模式,套用迭代的模板來一波“真香操作”。

模板解法的思路稍有不同,它先將根節點 cur 和所有的左孩子入棧並加入結果中,直至 cur 為空,用一個 while 循環實現:

然后,每彈出一個棧頂元素 tmp,就到達它的右孩子,再將這個節點當作 cur 重新按上面的步驟來一遍,直至棧為空。這里又需要一個 while 循環。

參考代碼如下:

2. 二叉樹的中序遍歷

LeetCode 題目:94. 二叉樹的中序遍歷

模板解法

和前序遍歷的代碼完全相同,只是在出棧的時候才將節點 tmp 的值加入到結果中。

3. 二叉樹的后序遍歷

LeetCode 題目:145. 二叉樹的后序遍歷

模板解法

繼續按照上面的思想,這次我們反着思考,節點 cur 先到達最右端的葉子節點並將路徑上的節點入棧;

然后每次從棧中彈出一個元素后,cur 到達它的左孩子,並將左孩子看作 cur 繼續執行上面的步驟。

最后將結果反向輸出即可。參考代碼如下:

然而,后序遍歷采用模板解法並沒有按照真實的棧操作,而是利用了結果的特點反向輸出,不免顯得技術含量不足。

因此掌握標准的棧操作解法是必要的。

常規解法

類比前序遍歷的常規解法,我們只需要將輸出的“根 -> 左 -> 右”的順序改為“左 -> 右 -> 根”就可以了。

如何實現呢?這里右一個小技巧,我們入棧時額外加入一個標識,比如這里使用 flag = 0

然后每次從棧中彈出元素時,如果 flag 為 0,則需要將 flag 變為 1 並連同該節點再次入棧,只有當 flag 為 1時才可將該節點加入到結果中。

參考代碼如下:

4. 二叉樹的層次遍歷

LeetCode 題目:102. 二叉樹的層序遍歷

二叉樹的層次遍歷的迭代方法與前面不用,因為前面的都采用了深度優先搜索的方式,而層次遍歷使用了廣度優先搜索,廣度優先搜索主要使用隊列實現,也就不能使用前面的模板解法了。

廣度優先搜索的步驟為:

  • 初始化隊列 q,並將根節點 root 加入到隊列中;
  • 當隊列不為空時:
    • 隊列中彈出節點 node,加入到結果中;
    • 如果左子樹非空,左子樹加入隊列;
    • 如果右子樹非空,右子樹加入隊列;

由於題目要求每一層保存在一個子數組中,所以我們額外加入了 level 保存每層的遍歷結果,並使用 for 循環來實現。

參考代碼如下:

總結

總結一下,在二叉樹的前序、中序、后序遍歷中,遞歸實現的偽代碼為:

迭代實現的偽代碼為:

掌握了以上基本的遍歷方式,對待更多的進階題目就游刃有余了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM