LeetCode 題目中,二叉樹的遍歷方式是最基本,也是最重要的一類題目,我們將從「前序」、「中序」、「后序」、「層序」四種遍歷方式出發,總結他們的遞歸和迭代解法。
題目說明
這里是 4 道相關題目:
二叉樹及遍歷方式
要解決這四道題目,最基本的前提是要了解什么是二叉樹,以及二叉樹的遍歷方式。如果你已經有所了解,則可以直接查看下一節的內容。
首先,二叉樹是一種「數據結構」,詳細的介紹可以參考力扣的「探索」卡片來進行學習。簡單來說,就是一個包含節點,以及它的左右孩子的一種數據結構。
如果對每一個節點進行編號,你會用什么方式去遍歷每個節點呢?
如果你按照 根節點 -> 左孩子 -> 右孩子
的方式遍歷,即「先序遍歷」,每次先遍歷根節點,遍歷結果為 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
循環來實現。
參考代碼如下:
總結
總結一下,在二叉樹的前序、中序、后序遍歷中,遞歸實現的偽代碼為:
迭代實現的偽代碼為:
掌握了以上基本的遍歷方式,對待更多的進階題目就游刃有余了。