递归方法下的二叉树的中序遍历比较简单,大概过程就是func(root->left)👉visit(root)👉func(root->right),如果不使用递归的方法的话,就要用栈来模拟这个过程。
将一个递归函数转换成为非递归的解决方法,最重要的就是搞明白在递归过程中函数栈的调用过程。我们可以思考一下内存状况:
每次函数执行,一路向左,只有当某棵左子树完全访问完才会访问根节点,之后在对根节点的右子树重复同样的过程,因此我们可以这样进行实现
1.当前结点是树根👉推入栈中👉去找它的左孩子
2.重复1,直到一个结点不是树根,也就是该节点为空,这意味着进入该结点的结点(父结点)的左子树完全访问完成,那么我们访问这个结点,完成中序过程。
3.我们访问完该结点的左子树和该结点,那么应该访问该结点的右子树,这也是同样的过程,只要走到它的右子树上即可,此时当前结点又成为了一个树根,重复1过程
代码如下:
Node* node = root;
stack<Node*> st;
while(node!=NULL||!st.empty()){
if(node!=null){
st.push(node);
node=node->left;
}
else{
node=st.top();
st.pop();
visit(node);
node=node->right;
}
}
类似的,我们可以写出前序的非递归:
Node* node = root;
stack<Node*> st;
while(node||!st.empty()){
if(node){
visit(node);
st.push(node);
node=node->left;
}
else{
node=st.top();
st.pop();
node=node->right;
}
}
后序遍历需要判断上次访问的节点是左子树还是右子树,如果该节点的左子树已被访问过则置标记为left;若右子树被访问过,则置标记为right:
//定义枚举类型:Tag
enum Tag{left,right};
//自定义新的类型,把二叉树节点和标记封装在一起
typedef struct
{
BTNode* node;
Tag tag;
}TagNode;
//后序遍历
void PostOrderWithoutRecursion2(BTNode* root)
{
if (root == NULL)
return;
stack<TagNode> s;
TagNode tagnode;
BTNode* p = root;
while (!s.empty() || p)
{
while (p)
{
tagnode.node = p;
//该节点的左子树被访问过
tagnode.tag = Tag::left;
s.push(tagnode);
p = p->lchild;
}
tagnode = s.top();
s.pop();
//左子树被访问过,则还需进入右子树
if (tagnode.tag == Tag::left)
{
//置换标记
tagnode.tag = Tag::right;
//再次入栈
s.push(tagnode);
p = tagnode.node;
//进入右子树
p = p->rchild;
}
else//右子树已被访问过,则可访问当前节点
{
cout << setw(4) << (tagnode.node)->data;
//置空,再次出栈(这一步是理解的难点)
p = NULL;
}
}
cout << endl;
}