三种二叉树遍历的非递归实现

今天偶然翻出了很久以前写的一篇博客,是关于三种二叉树遍历的非递归实现的,转到这儿来吧。程序都是伪代码,因为是考研复习期间写的,数据结构参考了严蔚敏的《数据结构》。

先看递归实现:

void Traverse(BiTree T){
    if(T){
    //visit,先序遍历
    Traverse(T->lchild);
    //visit,中序遍历
    Traverse(T->rchild);
    //visit,后序遍历
    }
}

可以看到三种遍历方法的递归实现形式完全一样,只需改变visit的位置,就得到不同遍历序列。因此从情感上觉得非递归实现应该形式也完全一样,这是课本给的中序非递归实现:

void InOrderTraverse(BiTree T, status(* visit)(TElemType e)){
    InitStack(s); p=T;
    while(p || !StackEmpty(s)){
        if(p){
        Push(s,p); p=p->lchild;
        }else{
        Pop(s,p);visit(p->data);p=p->rchild;
        }
    }
}

只需将visit(p->data);移动到Push(s,p); 后,就能得到先序遍历的非递归实现。然后在考虑后序遍历时,发现这种形式不适用后序遍历,所以后来不得不自己写了一个后序遍历的非递归程序。然而心里始终觉得不舒服,为什么递归实现上明明如此统一,到了非递归实现后序遍历就与众不同了呢?由于复习时间很紧张,对于这个问题基本都是零零散散的,走路蹲点发呆无聊的时候,漫无边际地胡思乱想~直到某天蹲点的时候,想明白了。

课本的非递归实现对栈的使用并不和递归栈相同,因此结点的进出栈顺序也和递归栈明显不同,基于这个道理,写了一个完全仿照递归栈工作的非递归实现,关键是出栈的条件发生了变化,而且从直觉上,这个程序的出栈和进栈语句(Push,Pop),赋值左孩子和右孩子(p=p->lchild或rchild)都应只出现一次,如果出现了多次,应该是功能重复了,可以再进行缩减。
status Traverse(BiTree T, status(* visit)(TElemType e)){
    IniTStack(s); p=T; p1=NULL;
    while(p || !StackEmpty(s)){
        if(p){
            Push(s,p);
            //visit,先序
            p=p->lchild;
        }else{
            GetTop(s,p);
            //visit,中序
            if(p->rchild==p1 || p->rchild==NULL){
                Pop(s,p1);p=NULL;
                //visit,后序
            }else{
                p=p->rchild;p1=NULL;
            }
        }
    }
}

在这种形式下,只需改变visit的位置就能得到三种遍历的非递归实现,结点的进出栈顺序完全和递归栈一样,判断结点是否出栈的条件是:上一次出栈的结点是栈顶元素的右孩子。

对几棵二叉树进行了试验,结果是对的,但不知道有没有疏忽的地方。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页