二叉樹的非遞歸遍歷
二叉樹的前序遍歷
題目描述
- 題目
給定一個二叉樹,返回它的 前序 遍歷。
-
示例:
-
輸入: [1,null,2,3]
1 \ 2 / 3
-
輸出: [1,2,3]
解題
- 思路
- 補充
前序遍歷:按照先遍歷根結點、再遍歷左子結點、最後遍歷右子節點的順序的訪問一棵樹的結點值
如下例
在遍歷某一層結點的時候需要我們在不同的時期去獲取同一層的不同結點地址
例如:上圖中的B,C結點本在同一層,但是因爲特殊的遍歷方式需要我們先去遍歷B的左右子樹再回來獲取C的結點地址.
鑑於該遍歷方式的特殊性,我們利用棧的 先進後出 特點來完成這個遍歷過程
由於我們需要先去遍歷左子樹再去遍歷右子樹,所以我們需要先將右子節點壓入棧中,再將左子節點壓入棧中.
需要注意在將棧頂元素彈出的時候需要將其右、左(先右後左)子節點也壓入棧中(若存在右左結點的話)
- 動圖演示
代碼實現
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr)
return ret;
stack<TreeNode*> s;
s.push(root);//先將根結點地址壓入棧中
while(!s.empty()){
TreeNode * head=s.top();
s.pop();
//先將根結點的值存儲到需要返回的數組中
ret.push_back(head->val);
//將根結點的右子樹壓入棧中
if(head->right)
s.push(head->right);
//將根結點的左子樹壓入棧中
if(head->left)
s.push(head->left);
}
return ret;
}
};
-
複雜度分析
-
時間複雜度:訪問每個節點恰好一次,時間複雜度爲 O(N),其中 N 是節點的個數,也就是樹的大小。
-
空間複雜度:取決於樹的結構,最壞情況存儲整棵樹,因此空間複雜度是 O(N)。
-
提交結果
二叉樹的中序遍歷
題目描述
- 題目
給定一個二叉樹,返回它的中序 遍歷。
-
示例:
-
輸入: [1,null,2,3]
1 \ 2 / 3
-
輸出: [1,3,2]
解題
-
補充
-
中序遍歷:先遍歷根結點的左孩子結點,再遍歷根結點,最後遍歷根結點的右孩子結點
-
如下例
-
可以看到我們需要最先輸出的結點是值爲D的結點,接着是其父親結點(這裏就引出了回退的問題,回退到其根結點),所以我們需要利用棧的 先進後出 的特點先將結點D的根結點B壓入棧中,在彈出結點值爲B的結點後需要將其右子節點(值爲E)壓入棧中.
-
循環執行上述步驟,直到棧爲空(既將二叉樹中的所有結點都壓到棧中)
-
動圖演示
代碼實現
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr){
return ret;
}
stack<TreeNode*> s;
TreeNode* cur=root;
while(cur!=nullptr || !s.empty()){
while(cur){
s.push(cur);
cur=cur->left;
}
cur=s.top();
s.pop();
ret.push_back(cur->val);
cur=cur->right;
}
return ret;
}
};
-
複雜度分析
-
空間複雜度:O(N)
-
時間複雜度:O(N)
-
運行結果
二叉樹的後序遍歷
題目描述
- 題目
給定一個二叉樹,返回它的 後序 遍歷。
-
示例:
-
輸入: [1,null,2,3]
1 \ 2 / 3
-
輸出: [3,2,1]
解題
-
補充
-
二叉樹的後序遍歷:先遍歷根結點的左孩子 再遍歷根結點的 右孩子, 最後遍歷根結點
-
如下例
從根節點開始依次迭代,彈出棧頂元素輸出到輸出列表中,然後依次壓入它的所有孩子節點(按照先左後右的順序壓入子節點),按照從上到下、從左至右的順序依次壓入棧中。
因爲深度優先搜索後序遍歷的順序是從下到上、從左至右,所以需要將輸出列表逆序輸出。
- 動圖演示
- 該過程是未逆置前的存儲結果
- 可以發現上述操作過後數組中存儲的值順序和後序遍歷的值順序相反,所以我們需要將數組中的值進行反轉
代碼實現
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
if(root==nullptr){
return ret;
}
stack<TreeNode*> s;
s.push(root);
while(!s.empty()){
TreeNode* head=s.top();
s.pop();
ret.push_back(head->val);
if(head->left){
s.push(head->left);
}
if(head->right){
s.push(head->right);
}
}
reverse(ret.begin(),ret.end());
return ret;
}
};
-
複雜度分析
-
時間複雜度:訪問每個節點恰好一次,因此時間複雜度爲 O(N),其中 N 是節點的個數,也就是樹的大小。
-
空間複雜度:取決於樹的結構,最壞情況需要保存整棵樹,因此空間複雜度爲 O(N)
-
運行結果