这篇是接着上篇的二叉树写的,上篇用递归实现了简单的二叉树,这次用非递归来实现二叉树的前序、中序、后序访问
要非递归实现,我们这里这里借助栈来实现,因为栈的后进先出特性,其实和递归的思想非常相似
栈:先入栈的最后出栈,最后入栈的最先出栈
递归:最先调用的最后释放,最后调用的最先释放
这样一比较是不是觉得其实递归的思想其实和栈的特性是一样的
铺垫了那么多进入今天的正题
1、前序
//非递归实现,借助栈实现
//递归思想与栈的后进先出特性
//前序
void PrevOrder_NonR()
{
_PrevOrder_NonR(_root);
}
void _PrevOrder_NonR(Node* root)
{
Node* cur = root;
stack<Node*> s;
while (cur || !s.empty())
//树还没遍历完
{
while (cur)
{
cout << cur->_data << " ";
s.push(cur);
cur = cur->_left;
}
//top从栈取出来,表示左子树已经访问完了,要开始访问右子树了
Node* top = s.top();
s.pop();
cur = top->_right;
}
}
循环将节点左孩子入栈并打印,当循环结束,代表左子树访问完了,开始右子树的访问
访问右子树,并Pop掉此时的top,因为top的作用就是用来找右子树的,这时它就已经完成了它的使命可以退出历史的舞台了,让它的前一个数暴露出来,成为新栈顶,带我们找下一个右子数
2、中序
中序和前序的方法一样,就是再打印数据时不同
//中序
void InOrder_NonR()
{
_InOrder_NonR(_root);
}
void _InOrder_NonR(Node* root)
{
Node* cur = root;
stack<Node*> s;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
//top从栈取出来,表示左子树已经访问完了,要开始访问右子树了
Node* top = s.top();
cout << top->_data << " ";
s.pop();
cur = top->_right;
}
}
3、后序
后序这里跟前面两个有点不太一样,因为后序是左->右->根
所以,我们再访问右子树的时候还不能Pop了根,但是,这样就会死循环,因为访问完右子树,返回到根时,就会又访问右子树,因为我们无法判断刚刚是否访问过右子树了,所以要加个判断条件,创建一个prev指针记录前节点,访问过就不再访问
//后序
void PostOrder_NonR()
{
_PostOrder_NonR(_root);
}
void _PostOrder_NonR(Node* root)
{
Node* cur = root;
stack<Node*> s;
Node* prev = NULL;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
Node* top = s.top();
//右子树为空或者右子树刚才访问过了,所以top的prev==top->_left
if (top->_right == NULL || top->_right == prev)
{
cout << top->_data << " ";
prev = top;
s.pop();
}
//访问右子树
else
{
cur = top->_right;
}
}