二叉樹:前中後序迭代方式統一寫法
此時我們在二叉樹:一入遞迴深似海,從此offer是路人中用遞迴的方式,實現了二叉樹前中後序的遍歷。
在二叉樹:聽說遞迴能做的,棧也能做!中用棧實現了二叉樹前後中序的迭代遍歷(非遞迴)。
之後我們發現「迭代法實現的先中後序,其實風格也不是那麼統一,除了先序和後序,有關聯,中序完全就是另一個風格了,一會用棧遍歷,一會又用指標來遍歷。」
實踐過的同學,也會發現使用迭代法實現先中後序遍歷,很難寫出統一的程式碼,不像是遞迴法,實現了其中的一種遍歷方式,其他兩種只要稍稍改一下節點順序就可以了。
其實「針對三種遍歷方式,使用迭代法是可以寫出統一風格的程式碼!」
「重頭戲來了,接下來介紹一下統一寫法。」
我們以中序遍歷為例,在二叉樹:聽說遞迴能做的,棧也能做!中提到說使用棧的話,「無法同時解決訪問節點(遍歷節點)和處理節點(將元素放進結果集)不一致的情況」。
「那我們就將訪問的節點放入棧中,把要處理的節點也放入棧中但是要做標記。」
如何標記呢,「就是要處理的節點放入棧之後,緊接著放入一個空指標作為標記。」 這種方法也可以叫做標記法。
迭代法中序遍歷
中序遍歷程式碼如下:(詳細註釋)
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop(); // 將該節點彈出,避免重複操作,下面再將右中左節點新增到棧中
if (node->right) st.push(node->right); // 新增右節點(空節點不入棧)
st.push(node); // 新增中節點
st.push(NULL); // 中節點訪問過,但是還沒有處理,加入空節點做為標記。
if (node->left) st.push(node->left); // 新增左節點(空節點不入棧)
} else { // 只有遇到空節點的時候,才將下一個節點放進結果集
st.pop(); // 將空節點彈出
node = st.top(); // 重新取出棧中元素
st.pop();
result.push_back(node->val); // 加入到結果集
}
}
return result;
}
};
看程式碼有點抽象我們來看一下動畫(中序遍歷):
中序遍歷迭代(統一寫法)
動畫中,result陣列就是最終結果集。
可以看出我們將訪問的節點直接加入到棧中,但如果是處理的節點則後面放入一個空節點, 這樣只有空節點彈出的時候,才將下一個節點放進結果集。
此時我們再來看前序遍歷程式碼。
迭代法前序遍歷
迭代法前序遍歷程式碼如下:(「注意此時我們和中序遍歷相比僅僅改變了兩行程式碼的順序」)
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
st.push(node); // 中
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
};
迭代法後序遍歷
後續遍歷程式碼如下:(「注意此時我們和中序遍歷相比僅僅改變了兩行程式碼的順序」)
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
};
總結
此時我們寫出了統一風格的迭代法,不用在糾結於前序寫出來了,中序寫不出來的情況了。
但是統一風格的迭代法並不好理解,而且想在面試直接寫出來還有難度的。
所以大家根據自己的個人喜好,對於二叉樹的前中後序遍歷,選擇一種自己容易理解的遞迴和迭代法。
本文:https://github.com/youngyangyang04/leetcode-master已經收錄,裡面還有leetcode刷題攻略、各個型別經典題目刷題順序、思維導圖,可以fork到自己倉庫,有空看一看一定會有所收穫,如果對你有幫助也給一個star支援一下吧!
我的B站(裡面有我講解的演算法視訊以及程式設計相關知識):https://space.bilibili.com/525438321
我是程式設計師Carl,哈工大師兄,先後在騰訊和百度從事技術研發多年,利用工作之餘重刷leetcode,更多 精彩演算法文章盡在: 程式碼隨想錄,關注後,回覆「Java」「C++」「python」「簡歷模板」等等,有我整理多年的學習資料,可以加我 微信,備註「個人簡介」+「組隊刷題」,拉你進入刷題群(無任何廣告,純個人分享),每天一道經典題目分析,我選的每一道題目都不是孤立的,而是由淺入深一脈相承的,如果跟住節奏每篇連續著看,定會融會貫通。
相關文章
- 【二叉樹】前中序求後序,中後序求前序二叉樹
- 二叉樹的前中後序遍歷二叉樹
- [資料結構]二叉樹的前中後序遍歷(遞迴+迭代實現)資料結構二叉樹遞迴
- 二叉樹的前序、中序、後序的遞迴和迭代實現二叉樹遞迴
- 二叉樹 前序、中序、後序二叉樹
- 【每日一題】二叉樹的前中後序非遞迴整理每日一題二叉樹遞迴
- 遞迴和迭代實現二叉樹先序、中序、後序和層序遍歷遞迴二叉樹
- 二叉樹 ---- 前序 中序 後序 知二求一二叉樹
- python-二叉樹:前、中、後、層序遍歷Python二叉樹
- 二叉樹迭代器(中序遞迴、前序和後序遍歷)演算法二叉樹遞迴演算法
- (樹)根據中序後序構建二叉樹二叉樹
- 還原二叉樹(先序+中序-〉後序)二叉樹
- 94. 二叉樹的中序遍歷(迭代)二叉樹
- 已知二叉樹的先序和後序求任意一中序二叉樹
- 二叉樹先知道後序和中序,求先序二叉樹
- 二叉樹中序和後序遍歷表示式二叉樹
- 二叉樹的先,中,後序遍歷二叉樹
- 二叉樹的先中後序遍歷二叉樹
- 二叉樹的前序、中序、後序三種遍歷二叉樹
- 二叉樹的前中後序遍歷(遞迴和非遞迴版本)二叉樹遞迴
- 二叉樹--後序遍歷二叉樹
- 根據二叉樹的先序序列和中序序列還原二叉樹並列印後序序列二叉樹
- 【演算法】二叉樹、N叉樹先序、中序、後序、BFS、DFS遍歷的遞迴和迭代實現記錄(Java版)演算法二叉樹遞迴Java
- 二叉樹的四種遍歷方法:先序,中序,後序,層序二叉樹
- 二叉樹的遍歷 (迭代法)二叉樹
- 二叉樹的前序,中序,後序遍歷方法總結二叉樹
- 從中序與後序遍歷序列構造二叉樹二叉樹
- 【樹01】對二叉樹前序/中序/後序遍歷演算法的一些思考二叉樹演算法
- 先序、中序、後序序列的二叉樹構造演算法二叉樹演算法
- 資料結構實驗之二叉樹八:(中序後序)求二叉樹的深度資料結構二叉樹
- 迭代二叉樹二叉樹
- 演算法 -- 實現二叉樹先序,中序和後序遍歷演算法二叉樹
- [資料結構] 根據前中後序遍歷中的兩種構造二叉樹資料結構二叉樹
- 二叉樹建立,前序遍歷,中序遍歷,後序遍歷 思路二叉樹
- 二叉樹的建立、前序遍歷、中序遍歷、後序遍歷二叉樹
- Java中用遞迴和迭代實現二叉樹的中序( InOrder )遍歷Java遞迴二叉樹
- 【根據前序和中序遍歷構造二叉樹】棧+迭代 || 遞迴二叉樹遞迴
- 144.二叉樹的前序遍歷145.二叉樹的後序遍歷 94.二叉樹的中序遍歷二叉樹