在上一篇博客中我们已经简单介绍过了二叉树的创建和一些基本操作以及对于先序,中序,后序的递归遍历,以及层序的非递归遍历,二叉树之创建与递归遍历,如果对递归遍历和层序的非递归有问题可以去看看;
本篇波可完整代码 请看二叉树
非递归遍历
先序非递归
我们先来回顾一下在什么是先序遍历;
先序遍历二叉树:
若二叉树为空,则空操作;否则:
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树;
这个在上次递归遍历中已经说过了,所以我们就直接可以来进行非递归遍历分析:
我们在考虑递归转到非递归问题的时候,一定会用到一个数据结构,它就是栈,为什么是栈呢? 因为在栈的特性就是后进先出和递归的思路差不多,所以我们用栈这个数据结构来代替递归,就把问题转化到非递归上面来:
我们先用看代码然后来解释为什么这么写?
先序非递归代码1:
void PreOrder_Nor()
{
cout << " PreOrder_Nor:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
s.push(pCur);
while (!s.empty())
{
Node* top = s.top();
cout << top->_data << " ";
s.pop();
if (top->_pRight)
s.push(top->_pRight);
if (top->_pLeft)
s.push(top->_pLeft);
}
}
因为先序遍历是根左右,栈是后进先出,所以在上面代码中,先存储了根结点,访问完后就pop掉,然后存储右结点在存储左结点,栈顶先访问的是左结点,就可以达到根左右的效果;
先序非递归代码2:
void PreOrder_Nor()
{
stack<Node*> s;
Node* pCur = _pRoot;
while (!s.empty() || pCur)
{
while (pCur)
{
cout << pCur->_data << " ";
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
s.pop();
pCur = top->_pRight;
}
}
在上面的代码中,我们可以看到是一上来就不管三七二十一先把他的左子树的根结点全先访问后入栈,然后在通过取栈顶元素来访问他的右节点;
中序非递归
中序遍历二叉树:
若二叉树为空,则空操作;否则:
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树;
和先序遍历一样,我们还是要利用栈这个数据结构来把递归转为非递归:
void InOrder_Nor()
{
cout << "InOrder_Nor:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
while (!s.empty() || pCur)
{
while (pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
s.pop();
cout << top->_data << " ";
pCur = top->_pRight;
}
}
在中序遍历的时候是左根右来遍历,所有我们在树中先找到最左边的叶子结点,从它开始访问,所以上述的while循环中我们已经找到了最左边的叶子结点,所以我们取栈顶元素就可以一步一步往回退着来访问,其中最后一句中我们是不用管它的右子树是否为空,因为在while(pCur)中可以判断;
后序非递归
后序遍历二叉树:
若二叉树为空,则空操作;否则:
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点;
void PostOrder_Nor()
{
cout << "PostOrder_Node:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
Node* prve = NULL; // 用来存放当前节点的前一个节点
while (!s.empty() || pCur)
{
while (pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
if (NULL == top->_pRight || top->_pRight == prve) //没有后面这个条件可能造成对一个有右节点的树死循环访问它的右子树
{
cout << top->_data << " ";
prve = top; //对它的前一个节点进行更新
s.pop();
}
else
pCur = top->_pRight;
}
}
咋后序遍历中,我们是左右根来遍历的,所以我们还是利用栈来存储结点,其中与其他不一样的是,在后序遍历中我们要yoga一个指针来保存上一个结点,不然的还在注释那一行如果树的右子树存在就会死循环,所以只要注意这一点,后序遍历并不难;