二叉树深度优先遍历总结
二叉树遍历是一个很常用的基础算法,尤其常用于作为递归转非递归算法的模板。其中,前序遍历常用作无返回值的自顶向下的递归算法的改写,后序遍历常用作带返回值的自底向上的递归算法的改写,例如求树的高度的两种思路。
这里对二叉树的非递归遍历做一个总结,前序、中序、后序使用的都是同一套模板。与递归写法一样,非递归写法也只有结点访问顺序的差别。
太长不看版(N-ary树更形象)
// 前序遍历
for (auto it=p->children.rbegin(); it != p->children.rend(); it++) {
stk.emplace(*it, false);
}
stk.emplace(p, true);
// 后序遍历
stk.emplace(p, true);
for (auto&& it=p->children.rbegin(); it !=p->children.rend(); it++) {
stk.emplace(*it, false);
}
递归遍历算法
前序遍历
vector<int> preorder(TreeNode* root, vector<int>& res) {
if (!root) return res;
res.push_back(root->val);
preorder(root->left, res);
preorder(root->right, res);
return res;
}
中序遍历
vector<int> inorder(TreeNode* root, vector<int>& res) {
if (!root) return res;
inorder(root->left, res);
res.push_back(root->val);
inorder(root->right, res);
return res;
}
后序遍历
vector<int> postorder(TreeNode* root, vector<int>& res) {
if (!root) return res;
postorder(root->left, res);
postorder(root->right, res);
res.push_back(root->val);
return res;
}
非递归遍历算法
采用模拟函数栈的方式,递归改非递归的思路:
- 栈是后入先出,所以右子节点先于左子结点入栈;
- 标记位记录是否已访问过,用于判断是否到达数据处理阶段/返回阶段;
可以改进的地方:
- 压栈前判空指针,减少出入栈次数;
- 前序遍历可以简化,无需标记位,因为新栈顶元素每次入栈后都是立即出栈;
前序遍历
void preorder(TreeNode *root, vector<int>& res)
{
if (root == nullptr) return;
stack< pair<TreeNode*, bool> > stk;
stk.emplace(root, false);
while(!stk.empty()) {
auto [p, visited] = stk.top();
stk.pop();
if(visited) {
res.push_back(p->val);
} else {
stk.emplace(p->right, false);
stk.emplace(p->left, false);
stk.emplace(p, true);
}
}
}
中序遍历
void inorder(TreeNode *root, vector<int>& res)
{
if (root == nullptr) return;
stack< pair<TreeNode*, bool> > stk;
stk.emplace(root, false);
while(!stk.empty()) {
auto [p, visited] = stk.top();
stk.pop();
if(visited) {
res.push_back(p->val);
} else {
stk.emplace(p->right, false);
stk.emplace(p, true);
stk.emplace(p->left, false);
}
}
}
后序遍历
void postorder(TreeNode *root, vector<int>& res)
{
if (root == nullptr) return;
stack< pair<TreeNode*, bool> > stk;
stk.emplace(root, false);
while(!stk.empty()) {
auto [p, visited] = stk.top();
stk.pop();
if(visited) {
res.push_back(p->val);
} else {
stk.emplace(p, true);
stk.emplace(p->right, false);
stk.emplace(p->left, false);
}
}
}