目录
- 144、二叉树的前序遍历
- 思路
- 代码
- 94、二叉树的中序遍历
- 思路
- 代码
- 145、后序遍历
- 思路
- 代码
- 总结
我们已经使用递归函数实现了二叉树的遍历。
递归函数实现的原理是每一次递归调用都会把函数的参数、局部变量、返回地址等压入调用栈中,然后递归返回时,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么会返回上一层位置的原因。
因此,在迭代实现二叉树遍历的过程中,我们可以使用栈来存储访问到的元素,并且辅助输出符合顺序的结果到结果集。
144、二叉树的前序遍历
思路
- 前序遍历:中左右
- 根据前序遍历的顺序,先将根节点压入栈中,将它的值加入结果集,然后弹出。
- 之后不断遍历,每轮遍历都将右子节点和左子节点压入栈中,将栈顶元素(左子节点)的值加入结果集,然后弹出。直到将所有的左子节点值全部放入结果集。
- 在之后便遍历中将之前存入栈中的右子节点值在依次加入结果集中。
代码
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;//结果集:存储顺序为前序遍历的顺序
if (root == NULL) return res;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();//获得栈顶元素
st.pop();//栈顶元素出栈
res.push_back(node -> val);//把栈顶元素的节点值加到结果集
if (node -> right) st.push(node -> right);//右节点入栈(为空时不入)
if (node -> left) st.push(node -> left);//左节点入栈(为空时不入)
}
return res;
}
};
94、二叉树的中序遍历
思路
- 中序遍历:左中右
- 根据中序遍历的顺序,我们需要先通过
root
访问到最底层的左节点,因此,初始化一个指针cur
初始指向root
。 - 使用
cur
从root
开始,向最底层的左子节点开始遍历,把遍历到的节点都压入栈中,cur
指向空时截至。 - 现在开始取出栈顶元素(将cur回退到栈顶元素),这时栈顶元素就是当前结果集需要的元素,把该元素值加入结果集,然后将
cur
指向当前节点的right
节点。 - 当
cur
指向空且栈中为空时,表明节点已经访问完且把所有节点值都按顺序加入结果集,循环截至。
代码
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
TreeNode* cur = root;//cur用来访问节点
while (cur != NULL || !st.empty()) {
if (cur != NULL) {//用cur访问节点,访问到最底层,把访问到的元素压入栈中
st.push(cur);
cur = cur -> left;//左
} else {//从栈中取出当前最底层的元素加入结果集
cur = st.top();//要处理的数据
st.pop();
res.push_back(cur -> val); //中
cur = cur -> right; //右
}
}
return res;
}
};
145、后序遍历
思路
- 后序遍历:左右中
- 前序遍历的顺序为中左右,我们只需要修改前序遍历得出结果的代码,是的代码运行的结果为中右左。
- 这时,将这个结果翻转,就能得到后序遍历的结果。
代码
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;//临时存放遍历过的节点
vector<int> res;//存放结果集
if (root == NULL) return res;
st.push(root);
//得到结果为“中右左”的结果集
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
res.push_back(node -> val);
if (node -> left) st.push(node -> left);
if (node -> right) st.push(node -> right);
}
reverse(res.begin(), res.end());//翻转结果集:左右中
return res;
}
};
总结
- 前序遍历和中序遍历的不同
- 前序遍历的顺序为中左右,在遍历访问节点的过程中就可以把元素值加入结果集;
- 而中序遍历的顺序为左中右,需要先到达最底层的左子节点在开始将元素加入结果集。
- 这决定了两种遍历方式在实现上的不同。
- 关于root节点为空的情况
- 前序遍历和后序遍历实现时都需要先将root节点压入栈中,如果root为空时,在执行之后把root节点值加入res时就会报错。因此,在这两个实现中,需要先判断root是否为空。
- 后序遍历因为要从root开始遍历整棵树,遍历的条件之一就是root不为空。因此,当root为空时,不会进入遍历树和填充结果集的逻辑,直接进行返回。