目录
结构
使用递归的方式时,将一个结点分为左子树,右子树,根。
我们使用非递归时,将树分为左路结点和右子树,同时在定义一个栈。先将左结点全放入栈中,根据栈的特性(后入先出),像递归一样从下先上遍历树。
前序遍历
当我们用前序非递归访问一棵树时,分为两步
1、左路节点:访问+入栈。
2、这些左路节点的右子树。
右子树通过迭代的方式再分成左路节点和左路节点的右子树的方式访问。也就是意味着当数据
出栈时,当前节点的左树就访问完了,当栈为空时,就结束。
vector<int> preorderTraversal(TreeNode* root)
{
vector<int> ret; //定义一个数组
stack<TreeNode*> st; //定义一个栈
TreeNode* cur=root;
while(cur || !st.empty())
{
//
while(cur) //访问左路结点并且入栈
{
ret.push_back(cur->val); //尾插数据
st.push(cur); //入栈
cur=cur->left;
}
TreeNode* top=st.top(); //找到最左边的数据
st.pop(); //出栈
cur=top->right; //迭代遍历右子树
}
return ret;
}
};
中序遍历
使用递归的思路时,将一个结点分为左子树,右子树,根,中序遍历就是 “根,左子树,右子树”
和前序类似,不过在左路节点入栈是,不访问数据
1、左路节点 (入栈)
2、取栈中的节点,(访问节点+访问节点的右子树)
代码的实现:
vector<int> inorderTraversal(TreeNode* root)
{
vector<int> vi;
stack<TreeNode*> st;
TreeNode* cur=root;
//循环迭代开始,cur指向节点,就是开始访问这棵树
while(cur || !st.empty())
{
//左路节点入栈
while (cur)
{
st.push(cur);
cur=cur->left;
}
//取出栈中节点,访问和节点的右子树
TreeNode* top=st.top();
st.pop();
vi.push_back(top->val);
cur=top->right;
}
return vi;
}
后序遍历
使用递归的思路时,将一个结点分为左子树,右子树,根,后序遍历就是 “左子树,右子树,根”
非递归时:只有不仅要先访问过当前结点的左结点还需要访问当前结点的右结点,最后才访问结点的数据,因此需要多定义一种指针(listnode)指向上一个访问的结点,当需要访问的结点的右节点等于listnode时,表示当前结点的右结点已经访问过了,便可以访问当前结点的数据。
和前面一样,分两步
1.左路结点入栈
2.在访问左路节点地右子树
代码实现:
vector<int> postorderTraversal(TreeNode* root)
{
vector<int> vi;
stack<TreeNode*> st;
TreeNode* lastNode = NULL; //最近访问的结点
TreeNode* cur= root;
while(cur || !st.empty())
{
while(cur) // //1.左路结点入栈
{
st.push(cur);
cur=cur->left;
}
//取到左路节点
TreeNode* top=st.top();
//如果左路节点的右为空或者上一个最近访问结点是右子树的跟,则表示右子树已经访问过了,可以
访问这个结点了,否则迭代器访问右子树
if(top->right==NULL || lastNode==top->right) //访问当前结点
{
vi.push_back(top->val);
lastNode=top;
st.pop();
}
//迭代器访问右子树
else
{
cur=top->right;
}
}
return vi;
}
我们发现非递归遍历一棵树时,大致都是相似的,不同点在于当我们访问当前结点的时机不同
本篇就分享到这里,如果本文对您有帮助请点赞支持一下~
文章尚有不足,欢迎大牛指正.