二叉樹:前中後序迭代方式統一寫法
此時我們在二叉樹:一入遞迴深似海,從此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】對二叉樹前序/中序/後序遍歷演算法的一些思考二叉樹演算法
- 資料結構實驗之二叉樹八:(中序後序)求二叉樹的深度資料結構二叉樹
- 迭代二叉樹二叉樹
- 演算法 -- 實現二叉樹先序,中序和後序遍歷演算法二叉樹
- [資料結構] 根據前中後序遍歷中的兩種構造二叉樹資料結構二叉樹
- 144.二叉樹的前序遍歷145.二叉樹的後序遍歷 94.二叉樹的中序遍歷二叉樹
- 144. 二叉樹的遍歷「前序、中序、後序」 Golang實現二叉樹Golang
- 【根據前序和中序遍歷構造二叉樹】棧+迭代 || 遞迴二叉樹遞迴
- Java中用遞迴和迭代實現二叉樹的中序( InOrder )遍歷Java遞迴二叉樹
- LeetCode 285 & 510. 二叉樹中序後繼 I & IILeetCode二叉樹
- LeetCode-106-從中序與後序遍歷序列構造二叉樹LeetCode二叉樹
- 二叉搜尋樹的後序遍歷序列
- 刷題系列 - 中序和後序遍歷佇列,構造對應二叉樹;佇列二叉樹
- 106. 從中序與後序遍歷序列構造二叉樹——Java實現二叉樹Java
- 二叉樹(順序儲存二叉樹,線索化二叉樹)二叉樹