【数据结构随笔】二叉树的前序,中序,后序非递归遍历
三种不同次序的二叉树遍历的递归算法结构相似,只是访问根节点以及遍历左子树、遍历右子树的先后次序不同而已。如果把访问根节点这个不涉及递归的语句抛开,则三个算法走过的路线是一样的。在递归执行的过程中,前序遍历情形是每进入一层 递归调用时先访问根节点,再依次访问它的左,右子树递归调用。中序遍历的情形是从左子树递归调用退出时访问根节点,然后向它的右子树执行递归调用。为把一个递归过程改为非递归过程,一般需要一个工作栈,记录遍历时的回退路径。
1、利用栈的前序遍历非递归算法
算法的基本思想:每访问一个结点前后,在向左子树遍历之前,利用栈记录这个结点的右子女(如果右子女不为空),以便左子树退回时可以直接从栈顶获取到右子树的根节点,继续其右子树的遍历访问。
代码:
1 void BinaryTree<T>::preOrder(void(*visit)(BinTreeNode<T>*p)){
2 BinTreeNode<T>* p=root;
3 stack<BinTreeNode<T>*> S;
4 while(p!=NULL){
5 visit(p);
6 if(p->rightChild) S.push(p->rightChild);
7 if(p->leftChild) p=p->leftChild;
8 else S.pop();
9 }
10 }
2、利用栈的中序遍历非递归算法
算法的基本思想:在一棵子树中首先访问的是中序下的第一个结点,它位于从根开始的沿leftChild链走到最左下的结点(不一定是叶结点),该节点的leftChild为NULL,访问它的数据后,再遍历该节点的右子树。如果某结点的右子树遍历完或右子树为空,则说明以这个结点为根的二叉树遍历完,此时从栈中退出更上层的结点并访问它,再向它的右子树遍历下去。
代码:
template<typename T>
void BinaryTree<T>::InOrder(void(*visit)(BinTreeNode<T>* p)){
stack<BinTreeNode<T>*> S;
BinTreeNode<T>* p=root; //temp为遍历指针
do{
while(p!=NULL){
S.push(p);
p=p->leftChild;
}
if(!S.empty()){
p=S.top();
S.pop();visit(p);
p=p->rightChild;
}
}while(p!=NULL||!S.empty())
3、利用栈的后序非递归算法
算法基本思想:在遍历完左子树时还不能访问根结点,需要再遍历右子树,待右子树遍历完时才访问根结点。所以栈工作中必须记录刚刚访问的是右子树(R)还是左子树(L),待右子树访问完后才访问根结点。所以栈工作中必须注明刚刚左子树还是右子树中。为此可定义栈结点的结构如下所示:
template<typename T>
struct stackNode{
BinTreeNode<T>* ptr; //指向树结点的指针
enum tag {L,R};
stackNode(BinTreeNode<T>* N=NULL):ptr(N),tag(L){ }
};
在算法中首先使用栈暂存根结点,再向左子树遍历下去,此时根结点tag=L。当访问完左子树中结点并从左子树退回时,还要去遍历根的右子树,此时改变根结点的tag=R。在从右子树中退出时才访问位于栈顶的根结点的值。
代码:
template<typename T>
void BinaryTreeNode<T>::postOrder(void(*visit)(BinTreeNode<T>*p)){
stack<stackNode<T>> S; stackNode<T> w;
BinTreeNode<T>* p=root;
do{
while(p!=NULL){
w.ptr=p;w.tag=L;S.push(w);
p=p->leftChild;
}
int continuel=1; //继续循环的标记,用于遍历完左子树后的右子树遍历;
while(continuel&&!S.empty()){
S.pop();p=w.ptr;
switch(w.tag){
case L:w.tag=R;S.push(w);
continuel=0;
p=p->rightChild;
break;
case R:visit(p);break;
}
}while(!S.empty())

浙公网安备 33010602011771号