二叉树的非递归遍历

 

  二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序、中序以及后序三种遍历方法。因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。

一.前序遍历

   前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。

   1.递归实现

复制代码
void preorder(BiTNode *B) //先序递归
{
if(B!=NULL)
{
printf("%c",B->data);
preorder(B->lchild);
preorder(B->rchild);
}
}
复制代码

   2.非递归实现

    根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:

     对于任一结点P:

     1)访问结点P,并将结点P入栈;

     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

     3)直到P为NULL并且栈为空,则遍历结束。

复制代码
void Preorder(BiTNode *B,SeqStack K)   //遍历二叉树函数
{
    printf("先序遍历结果为:");    //提示以下结果为先序遍历结果
BiTNode *p=B;        //p指针指向当前结点
while(p!=NULL||K.top!=-1)
{
if(p!=NULL)
{
printf("%c",p->data);
K.top++;
       K.pin[K.top]=p;
p=p->lchild;
}
else
{
       p=K.pin[K.top];
       K.top--;
p=p->rchild;
}
}
  printf("\n");
}
复制代码

二.中序遍历

    中序遍历按照“左孩子-根结点-右孩子”的顺序进行访问。

    1.递归实现

复制代码
void inorder(BiTNode *B) //中序递归
{
if(B!=NULL)
{
inorder(B->lchild);
printf("%c",B->data);
inorder(B->rchild);
}
}
复制代码

   2.非递归实现

    根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:

   对于任一结点P,

  1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;

  2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;

  3)直到P为NULL并且栈为空则遍历结束

复制代码
void InOrder(BiTNode *B,SeqStack K)      //非递归中序遍历
{
printf("中序遍历结果为:");
BiTNode *p=B;
while(p!=NULL||K.top!=-1)
{
if(p!=NULL)
{
K.top++;
       K.pin[K.top]=p;
p=p->lchild;
}
else
{
p=K.pin[K.top];
printf("%c",p->data);
K.top--;
p=p->rchild;
}
}
  printf("\n");
}
复制代码

  三.后序遍历

      后序遍历按照“左孩子-右孩子-根结点”的顺序进行访问。

      1.递归实现

复制代码
void postorder(BiTNode *B) //后序递归
{
if(B!=NULL)
{
postorder(B->lchild);
postorder(B->rchild);
printf("%c",B->data);
}
}
复制代码

      2.非递归实现

       后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。下面介绍两种思路。

      第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。

复制代码
void postOrder2(BinTree *root)    //非递归后序遍历
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子树一直往下搜索,直至出现没有左子树的结点
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出现在栈顶
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else //第二次出现在栈顶
{
cout<<temp->btnode->data<<" ";
p=NULL;
}
}
}
}
复制代码

        第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

复制代码

void PostOrder(BiTNode *B,SeqStack K) //遍历二叉树函数
{
   printf("后序遍历结果为:"); //提示以下结果为先序遍历结果
  BiTNode *p=B,*pre=NULL; //p指针指向当前结点
  K.top++;
  K.pin[K.top]=p; //令当前结点指针p进栈
  while(K.top!=-1) //当栈不为空时执行以下操作
  {
    p=K.pin[K.top];
    if((p->lchild==NULL&&p->rchild==NULL)||(pre!=NULL&&(pre==p->lchild||pre==p->rchild)))
      {
        printf("%c",p->data);
        K.top--;
        pre=p;
      }
    else
    {
       if(p->rchild!=NULL)
       {
          K.top++;
          K.pin[K.top]=p->rchild;
       }
       if(p->lchild!=NULL)
       {
          K.top++;
          K.pin[K.top]=p->lchild;
       }
    }
  }
  printf("\n");
}

复制代码

 四.整个程序完整的代码

#include<stdio.h>
#include<stdlib.h>
typedef struct node   //二叉树结点的类型描述
{
    char data;       //data用于存储二叉树中的字母
    struct node *lchild; //lchild为指向该结点左孩子的指针
    struct node *rchild; //rchild为指向该结点下一层的指针
} BiTNode;
typedef struct       //顺序栈的类型描述
{
    BiTNode *pin[100];  //指针数组,
    int top;        //栈顶指针
} SeqStack;
void Create(BiTNode *B,SeqStack K,char s[]) //建立二叉树的函数
{
    BiTNode *p,*q;        //p指针指向当前结点,q指针指向新申请的结点
    int j;                   //j用于标记输入的字符在数组中的位置
    printf("输入二叉树的括号表示形式:\n");  //提示输入二叉树
    gets(s);
    p=B;                //当前结点为根结点
    p->lchild=NULL;
    p->rchild=NULL;    //根结点的lchild域和rchild域为空
    for(j=0; s[j]!='\0'; j++)         //进入循环,建立二叉树
    {
        if(s[j]=='(')        //若字符为'(',执行以下操作
        {
            if(s[j+1]==',')      //若'('的下一个字符为',',当前结点p的lchild域为空
                p->lchild=NULL;
            else              //若'('的下一个字符不为','
            {
                q=(BiTNode *)malloc(sizeof(BiTNode));  //申请新结点q
                q->lchild=NULL;
                q->rchild=NULL;    //令新结点q的lchild域和rchild域为空
                p->lchild=q;               //令当前结点p的lchild域指向新申请的结点 q
                p=q;                   //将新申请的结点q作为新的当前结 点p
            }
        }
        else
        {
            if(s[j]==',')                 //若字符为',',执行以下操作
            {
                p=K.pin[K.top];               //令当前结点p为栈顶元素,但不退栈
                q=(BiTNode *)malloc(sizeof(BiTNode));  //申请新结点q
                q->lchild=NULL;
                q->rchild=NULL;        //令新结点q的lchild域和rchild域为空
                p->rchild=q;        //令当前结点p的lchild域指向新申请的结点 q
                p=q;          //将新申请的结点q作为新的当前结 点p
            }
            else
            {
                if(s[j]==')')    //若字符为')',执行以下操作
                {
                    p=K.pin[K.top];     //出栈,令当前结点p为栈顶元素
                    K.top--;
                }
                else                //若字符为'字母',执行以下操作
                {
                    p->data=s[j];   //令当前结点p的data域为该字母
                    if(s[j+1]=='(')    //若该字母的下一个字符为'('则令当前结点指针p进栈
                    {
                        K.top++;
                        K.pin[K.top]=p;
                    }
                }
            }
        }
    }
    printf("\n");
}

void Preorder(BiTNode *B,SeqStack K)   //遍历二叉树函数
{
    printf("先序遍历结果为:");    //提示以下结果为先序遍历结果
    BiTNode *p=B;        //p指针指向当前结点
    K.top++;          //令当前结点指针p进栈
    K.pin[K.top]=p;
    while(K.top!=-1)            //当栈不为空时执行以下操作
    {
        p=K.pin[K.top];           //出栈,栈顶元素所指的结点作为当前结点p
        K.top--;
        printf("%c",p->data);    //输出当前结点p中的字母
        if(p->rchild!=NULL)         //若当前结点p的右孩子不为空,则令当前结点p的右孩子进栈
        {
            K.top++;
            K.pin[K.top]=p->rchild;
        }
        if(p->lchild!=NULL)        //若当前结点p的左孩子不为空,则令当前结点p的左孩子进栈
        {
            K.top++;
            K.pin[K.top]=p->lchild;
        }
    }
    printf("\n");
}

void preorder(BiTNode *B) //先序递归
{
    if(B!=NULL)
    {
        printf("%c",B->data);
        preorder(B->lchild);
        preorder(B->rchild);
    }
}

void InOrder(BiTNode *B,SeqStack K)
{
    printf("中序遍历结果为:");
    BiTNode *p=B;
    while(p!=NULL||K.top!=-1)
    {
        if(p!=NULL)
        {
            K.top++;
            K.pin[K.top]=p;
            p=p->lchild;
        }
        else
        {
            p=K.pin[K.top];
            printf("%c",p->data);
            K.top--;
            p=p->rchild;
        }
    }
    printf("\n");
}

void inorder(BiTNode *B) //中序递归
{
    if(B!=NULL)
    {
        inorder(B->lchild);
        printf("%c",B->data);
        inorder(B->rchild);
    }
}

void PostOrder(BiTNode *B,SeqStack K)   //遍历二叉树函数
{
    printf("后序遍历结果为:");    //提示以下结果为先序遍历结果
    BiTNode *p=B,*pre=NULL;        //p指针指向当前结点
    K.top++;
    K.pin[K.top]=p;             //令当前结点指针p进栈
    while(K.top!=-1)            //当栈不为空时执行以下操作
    {
        p=K.pin[K.top];
        if((p->lchild==NULL&&p->rchild==NULL)||
                (pre!=NULL&&(pre==p->lchild||pre==p->rchild)))
        {
            printf("%c",p->data);
            K.top--;
            pre=p;
        }
        else
        {
            if(p->rchild!=NULL)
            {
                K.top++;
                K.pin[K.top]=p->rchild;
            }
            if(p->lchild!=NULL)
            {
                K.top++;
                K.pin[K.top]=p->lchild;
            }
        }
    }
    printf("\n");
}

void postorder(BiTNode *B) //后序递归
{
    if(B!=NULL)
    {
        postorder(B->lchild);
        postorder(B->rchild);
        printf("%c",B->data);
    }
}
int main()
{
    char s[40];    //定义数组,存储输入的字符串
    int i;
    BiTNode *B;     //定义根结点,并申请存储空间
    B=(BiTNode *)malloc(sizeof(BiTNode));
    SeqStack K;    //定义栈并初始化栈
    K.top=-1;
    Create(B,K,s);          //调用建立二叉树的函数
    if(s[0]!='\0')
    {
        Preorder(B,K);//先序遍历
        printf("递归先序遍历为:");
        preorder(B);
        printf("\n");
        InOrder(B,K);//中序遍历
        printf("递归中序遍历为:");
        inorder(B);
        printf("\n");
        PostOrder(B,K);//后序遍历
        printf("递归后序遍历为:");
        postorder(B);//后序遍历检测
        printf("\n");
    }
    else printf("二叉树未建立\n"); //若输入为空,则输出该提示

    return 0;
}
//测试数据:
//-(+(a,*(b,-(c,d))),/(e,f))
DFS.c

 

posted @ 2014-11-12 21:05  CodeNoob  阅读(227)  评论(0编辑  收藏  举报