前言
前面学习了递归实现二叉树的方法,而递归的定义就是: 每一次递归调用,都会把函数的局部变量、参数值和返回地址等压入调用栈中。 然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
那么,可以用非递归的方式实现吗? 答案是可以。
所以,用栈(非递归的方式)可以实现二叉树的前中后序遍历。
前序遍历
这里引用一下大佬的动图,来总结一下前序遍历的规律
以上用例的前序遍历是:5、4、1、2、6 (中、左、右)
在入栈时, ①先读取根节点,取值,然后直接将根节点出栈。 ②读取右节点、再读取左节点 ,取值,出栈
因为要考虑出栈顺序,符合前序遍历。所以入栈顺序是中、右、左。
代码如下:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;//1、建栈
vector<int> result;//2、存前序遍历结果
st.push(root);//读根节点,用push
while (!st.empty()) {
//根节点不为空,入栈
TreeNode* node = st.top(); // 中
st.pop();
//根节点不为空,存入前序遍历中
//ps: 对象存元素,用. 指针访问元素,用->
if (node != NULL) result.push_back(node->val);
//为空就走下一个节点元素
else continue;
//规定下一个节点元素是什么
st.push(node->right); // 右
st.push(node->left); // 左
}
return result;
}
};
中序遍历
写中序遍历的时候,就不能上面的代码了。
因为,前序遍历时,先要处理的元素,和先要访问的元素,顺序都是一致的,都是中间节点。
而中序遍历是(左、中、右)跟入栈的顺序完全相反。
所以, 这时候要借助指针的遍历,帮助访问节点。而栈用来处理节点上的元素。
这里引用一下大佬的动图,来总结一下中序遍历的规律
以上用例的中序遍历是:1、4、2、5、6 (左、中、右)
在入栈时, ①用node指针一直访问到二叉树左子树为空的时候,每成功访问一课左子树,就入栈 ②当左子树为空,栈不为空时,说明要开始中序遍历:栈顶值给当前指针,出栈,访问右子树。依此循环
代码如下:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> s1;//栈
TreeNode* node;
node = root;//根节点
vector<int> result;//存中序遍历
//栈不为空,存在根节点
//node不为空,存在根节点下的右子树
while(!s1.empty() || node!=nullptr){
//1、没达到左子树底端
if(node!=nullptr){
s1.push(node);//2、先入栈
node =node->left;//3、接着访问左子树
}else{
node = s1.top();//取栈顶
result.push_back(node->val);//存元素
s1.pop();//出栈
node=node->right;//寻找右子树
}
}
return result;
}
};
后序遍历
后续遍历是左右中。
那么我们只需要:
①调整一下先序遍历的代码顺序,就变成中右左的遍历顺序。
②然后在反转result数组,输出的结果顺序就是左右中了。
如下图:
前序遍历代码一改,美滋滋。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s1;//只能读节点
vector<int> result;
TreeNode* node;
s1.push(root);
while(!s1.empty()){
node =s1.top();
s1.pop();
if(node!=NULL){
result.push_back(node->val);
s1.push(node->left);
s1.push(node->right);
}else
continue;
// s1.push(node->left); 魔改的前序,效果都是一样的
// s1.push(node->right);
}
reverse(result.begin(),result.end());//将魔改的前序反转了
return result;
}
};