二叉树的前序遍历(迭代法)
1、题目
题目链接:144. 二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[1,2]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
- 树中节点数目在范围 [0, 100] 内
- -100 <= Node.val <= 100
2、思路
我们也可以用迭代的方式实现的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。
3、代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> stk;
// 如果根节点不为空,则将根节点入栈
if (root != nullptr) stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top();
if (node != nullptr) {
stk.pop();
// 如果右子节点不为空,则将右子节点入栈
if (node->right) stk.push(node->right);
// 如果左子节点不为空,则将左子节点入栈
if (node->left) stk.push(node->left);
// 将当前节点重新入栈,并标记为已访问(nullptr)
stk.push(node);
stk.push(nullptr);
} else {
// 弹出标记为已访问的节点(nullptr)
stk.pop();
// 取出栈顶的节点
node = stk.top();
// 弹出栈顶的节点
stk.pop();
// 将节点的值加入结果数组
result.push_back(node->val);
}
}
return result;
}
};
int main() {
Solution s;
TreeNode* root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3));
vector<int> res = s.preorderTraversal(root);
for (int i : res) {
cout << i << " ";
}
cout << endl;
return 0;
}
4、复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
- 空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。
二叉树的中序遍历(迭代法)
1、题目
题目链接:94. 二叉树的中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
提示:
- 树中节点数目在范围 [0, 100] 内
- -100 <= Node.val <= 100
2、思路
我们也可以用迭代的方式实现的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。
3、代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> stk;
if (root != nullptr) stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top();
if (node != nullptr) {
stk.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
if (node->right) stk.push(node->right); // 添加右节点(空节点不入栈)
stk.push(node); // 添加中节点
stk.push(nullptr); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node->left) stk.push(node->left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
stk.pop(); // 将空节点弹出
node = stk.top(); // 重新取出栈中元素
stk.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
};
int main() {
Solution s;
TreeNode* root = new TreeNode(1, new TreeNode(2, new TreeNode(3), new TreeNode(4)), new TreeNode(5));
vector<int> res = s.inorderTraversal(root);
for (int i : res) {
cout << i << " ";
}
cout << endl;
return 0;
}
4、复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
- 空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。
二叉树的后序遍历(迭代法)
1、题目
题目链接:145. 二叉树的后序遍历
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[3,2,1]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
提示:
- 树中节点的数目在范围 [0, 100] 内
- -100 <= Node.val <= 100
2、思路
我们也可以用迭代的方式实现的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。
3、代码
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> stk;
if (root != nullptr) stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top();
if (node != nullptr) {
// 当前节点不为空
stk.pop();
stk.push(node); // 再次将当前节点入栈
stk.push(nullptr); // 将nullptr入栈,用于区分节点的左右子树是否已经被遍历
if (node->right) stk.push(node->right); // 如果右子节点不为空,则将右子节点入栈
if (node->left) stk.push(node->left); // 如果左子节点不为空,则将左子节点入栈
} else {
// 当前节点为空,表示上一个节点已经遍历了其左子树和右子树
stk.pop();
node = stk.top(); // 获取上一个节点
stk.pop(); // 弹出上一个节点
result.push_back(node->val); // 将上一个节点的值加入结果集中
}
}
return result;
}
};
int main() {
Solution s;
TreeNode* root = new TreeNode(1, new TreeNode(2, new TreeNode(4), new TreeNode(5)), new TreeNode(3, new TreeNode(6), new TreeNode(7)));
vector<int> res = s.postorderTraversal(root);
for (int i : res) {
cout << i << " ";
}
cout << endl;
return 0;
}
4、复杂度分析
- 时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。
- 空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。