二叉树的层次遍历要用到队列,前序、中序和后序非递归遍历要用到栈,这里使用C++标准库中的容器适配器queue和stack。
1、 二叉树的层次遍历
二叉树的层次遍历类似图的广度优先遍历,都用到了队列。
基本思路:
(1)根结点非空,则入队列
(2)队列非空,队首元素出队列,输出结点值,若结点有左孩子,左孩子入队列;若结点有右孩子,右孩子也入队列。
(3)重复步骤(2)直到队列为空
显然通过以上步骤,得到的是二叉树从上至下,从左到右的层次遍历序列。
实现代码如下:
//二叉树的层次遍历,用到了队列
void levelOrderTraverse(const BiTree& T)
{
queue<BiTree> q;
BiTree p = NULL;
if(T)//若根结点非空,则入队列
{
q.push(T);
}
while(!q.empty())//队列非空
{
p = q.front();
q.pop();
cout<<p->data<<" ";
if(p->lchild)//左孩子不空,入队列
{
q.push(p->lchild);
}
if(p->rchild)//右孩子不空,入队列
{
q.push(p->rchild);
}
}
}
2、二叉树的先序非递归遍历
二叉树的先序非递归遍历比较简单,基本思路是先输出结点值,再入栈,然后遍历左子树。退栈时,遍历栈顶结点的右子树,看代码:
//二叉树的先序非递归遍历
void iterativePreOrderTraverse(const BiTree& T)
{
stack<BiTree> s;
BiTree p = T;
while(p || !s.empty())//p非空,或栈非空
{
if(p)
{
//输出根结点,根结点入栈,遍历左子树
cout<<p->data<<" ";
s.push(p);
p = p->lchild;
}
else
{
//根结点退栈,遍历右子树
p = s.top();
s.pop();
p = p->rchild;//转右孩子结点
}
}
}
3、二叉树的中序非递归遍历
二叉树的中序非递归遍历也很简单,和先序不同的是,中序是先进栈,再遍历左子树;出栈时,才输出结点值,然后遍历右子树,代码如下:
//二叉树的中序非递归遍历
void iterativeInOrderTraverse(const BiTree& T)
{
stack<BiTree> s;
BiTree p = T;
while(p || !s.empty())//p非空,或栈非空
{
if(p)
{
//根指针进栈 ,遍历左子树
s.push(p);
p = p->lchild;
}
else
{
//根指针退栈,访问根结点,遍历右子树
p = s.top();
s.pop();
cout<<p->data<<" ";
p = p->rchild;
}
}
}
4.二叉树的后序非递归遍历
二叉树的后序非递归遍历,比先序和中序非递归遍历,稍微复杂点。因为后序遍历的顺序是左子树->右子树->根结点,所以我们在遍历过程中得标记左右子树的状态。这里我们用一个bool变量标识结点p的右子树遍历状态,false表示右子树未遍历,true则表示右子树已遍历。代码如下:
(1)版本1
//二叉树的后序非递归遍历
void iterativePostOrderTraverse(const BiTree& T)
{
stack<pair<BiTree,bool> > s;
BiTree p = T;
while(p || !s.empty())
{
if(p)
{
s.push(make_pair(p,false));//false表示根结点p的右子树未访问
p = p->lchild;
}
else
{
if(s.top().second == false)//根结点的右子树未访问
{
s.top().second = true;//标志右子树为已访问
p = s.top().first->rchild;//遍历右子树
}
else//右子树已访问
{
cout<<s.top().first->data<<" "; //输出根结点值
s.pop();//根结点出栈
}
}
}
}
(2)版本2
//后序非递归遍历的另一个版本
void anotherIterativePostOrderTraverse(const BiTree& T)
{
stack<pair<BiTree,bool> > s;
BiTree p = T;
do
{
while(p)
{
s.push(make_pair(p,false));
p = p->lchild;
}
while(!s.empty() && s.top().second == true)
{
cout<<s.top().first->data<<" ";
s.pop();
}
if(!s.empty())
{
s.top().second = true;
p = s.top().first->rchild;
}
}while(!s.empty());
}
注:我们来分析一下,后序非递归遍历的执行过程。举一个简单的例子吧,使用根结点为A,其左右子树分别为B、C的树来讲解版本(1)的执行过程。
(1)初始时,p =T,接着执行循环,若根结点非空,则<p,false>入栈,表示根结点p的右子树还未遍历,同时置p为其左指针,遍历左子树。若根结点为空(此时栈也为空),循环条件不满足,直接结束。
(2)在接下来的循环中,若p为空则看栈顶结点状态。若其右子树未遍历,则遍历其右子树(p值发生改变),否则直接输出结点值,并弹出栈顶结点(p值未改变)。
(3)p=A,<A,false>入栈,p=A->lchild=B;
p=B,<B,false>入栈,p=NULL;
p=NULL,栈非空,且栈顶结点B的右子树未访问,置true,然后访问B的右子树,p=B->rchild=NULL;
p=NULL,栈非空,且栈顶结点B的右子树已访问,输出结点B,弹出栈顶结点B,注意此时p==NULL,因为这个过程p的值未改变。
p=NULL,栈非空,且此时栈顶结点A的右子树未访问,置true,然后访问A的右子树,p=A->rchild=C;
p=C,非空,<C,false>入栈,p = C->lchild=NULL;
p=NULL,栈非空,栈顶元素C的右子树为访问,置true,然后访问C的右子树,p=C->rchild=NULL;
p=NULL,栈非空,栈顶元素C的右子树已访问,输出结点C,弹出栈顶元素C。
p=NULL,栈非空,栈顶元素A的右子树已访问,输出结点A,弹出栈顶元素A。
p=NULL,此时栈已为空。
(4)注意到在上述的分析过程中,我们发现当p的值为NULL,出栈,p的值未改变,此时我们不用判断p的值,可以直接判断栈顶元素的右子树的访问状态,于是调整循环结构就得到了版本(2)。但是它们原理都是一样的。
5、完整测试代码
#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>
using namespace std;
//二叉树定义
typedef char ElementType;
typedef struct BiTreeNode
{
ElementType data;
struct BiTreeNode* lchild;
struct BiTreeNode* rchild;
}BiTreeNode, *BiTree;
//递归的建立一棵二叉树
//输入为二叉树的先序序列
void createBiTree(BiTree &T)
{
char data;
data = getchar();
if(data == '#')
{
T = NULL;
}
else
{
T = new BiTreeNode;
T->data = data;
createBiTree(T->lchild);
createBiTree(T->rchild);
}
}
//递归销毁一棵二叉树
void destroyBiTree(BiTree &T)
{
if(T)
{
destroyBiTree(T->lchild);
destroyBiTree(T->rchild);
delete T;
T = NULL;
}
}
//二叉树的层次遍历,用到了队列
void levelOrderTraverse(const BiTree& T)
{
queue<BiTree> q;
BiTree p = NULL;
if(T)//若根结点非空,则入队列
{
q.push(T);
}
while(!q.empty())//队列非空
{
p = q.front();
q.pop();
cout<<p->data<<" ";
if(p->lchild)//左孩子不空,入队列
{
q.push(p->lchild);
}
if(p->rchild)//右孩子不空,入队列
{
q.push(p->rchild);
}
}
}
//二叉树的先序非递归遍历
void iterativePreOrderTraverse(const BiTree& T)
{
stack<BiTree> s;
BiTree p = T;
while(p || !s.empty())//p非空,或栈非空
{
if(p)
{
//输出根结点,根结点入栈,遍历左子树
cout<<p->data<<" ";
s.push(p);
p = p->lchild;
}
else
{
//根结点退栈,遍历右子树
p = s.top();
s.pop();
p = p->rchild;//转右孩子结点
}
}
}
//二叉树的中序非递归遍历
void iterativeInOrderTraverse(const BiTree& T)
{
stack<BiTree> s;
BiTree p = T;
while(p || !s.empty())//p非空,或栈非空
{
if(p)
{
//根指针进栈 ,遍历左子树
s.push(p);
p = p->lchild;
}
else
{
//根指针退栈,访问根结点,遍历右子树
p = s.top();
s.pop();
cout<<p->data<<" ";
p = p->rchild;
}
}
}
//二叉树的后序非递归遍历
void iterativePostOrderTraverse(const BiTree& T)
{
stack<pair<BiTree,bool> > s;
BiTree p = T;
while(p || !s.empty())
{
if(p)
{
s.push(make_pair(p,false));//false表示根结点p的右子树未访问
p = p->lchild;
}
else
{
if(s.top().second == false)//根结点的右子树未访问
{
s.top().second = true;//标志右子树为已访问
p = s.top().first->rchild;//遍历右子树
}
else//右子树已访问
{
cout<<s.top().first->data<<" "; //输出根结点值
s.pop();//根结点出栈
}
}
}
}
//后序非递归遍历的另一个版本
void anotherIterativePostOrderTraverse(const BiTree& T)
{
stack<pair<BiTree,bool> > s;
BiTree p = T;
do
{
while(p)
{
s.push(make_pair(p,false));
p = p->lchild;
}
while(!s.empty() && s.top().second == true)
{
cout<<s.top().first->data<<" ";
s.pop();
}
if(!s.empty())
{
s.top().second = true;
p = s.top().first->rchild;
}
}while(!s.empty());
}
int main(int argc, char *argv[])
{
BiTree T = NULL;
createBiTree(T);//建立二叉树 如输入AB#D##CE###
// createBiTreeWithGenList(T);//如输入A(B(,D),C(E))#
cout<<"levelOrder: ";
levelOrderTraverse(T);
cout<<endl;
cout<<"preOrder: "; //先序遍历
// preOrderTraverse(T);
iterativePreOrderTraverse(T);
cout<<endl;
cout<<"inOrder: ";//中序遍历
// inOrderTraverse(T);
iterativeInOrderTraverse(T);
cout<<endl;
cout<<"postOrder: ";//后序遍历
// postOrderTraverse(T);
// iterativePostOrderTraverse(T);
anotherIterativePostOrderTraverse(T);
cout<<endl;
destroyBiTree(T);//销毁二叉树,释放空间
system("PAUSE");
return EXIT_SUCCESS;
}
转自:http://blog.csdn.net/sysu_arui/article/details/7865873