zoukankan      html  css  js  c++  java
  • 二叉树的非递归遍历(含有详细解释)

    看了一些资料,对于二叉树的非递归实现原理不是很理解,下面的博客中给出了一些解释,收藏下来:

    http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html

     二叉树的非递归遍历

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

    一.前序遍历

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

       1.递归实现

    复制代码
    void preOrder1(BinTree *root)     //递归前序遍历 {
        if(root!=NULL)
        {
            cout<<root->data<<"";
            preOrder1(root->lchild);
            preOrder1(root->rchild);
        }
    }
    复制代码

       2.非递归实现

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

    对于任一结点P:

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

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

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

    对于处理过程的解释,感觉原来博客说的还不是很清楚,个人理解如下:

         对于任一结点P:

         1)访问结点P,并将结点P入栈,判断结点P的左孩子是否为空,若非空,则将P的左孩子置为当前的结点P,循环步骤1);若为空,转至步骤2);

         2)取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,转至步骤3);

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

    复制代码
    void preOrder2(BinTree *root)     //非递归前序遍历 {
        stack<BinTree*> s;
        BinTree *p=root;
        while(p!=NULL||!s.empty())
        {
            while(p!=NULL)
            {
                cout<<p->data<<"";
                s.push(p);
                p=p->lchild;
            }
            if(!s.empty())
            {
                p=s.top();
                s.pop();
                p=p->rchild;
            }
        }
    }
    复制代码

    二.中序遍历

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

        1.递归实现

    复制代码
    void inOrder1(BinTree *root)      //递归中序遍历{
        if(root!=NULL)
        {
            inOrder1(root->lchild);
            cout<<root->data<<"";
            inOrder1(root->rchild);
        }
    } 
    复制代码

       2.非递归实现

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

     对于任一结点P,

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

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

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

    初学非递归,对于处理过程的解释,感觉原来博客说的还不是很清楚,个人理解如下:

       对于任一结点P,

      1)若P不为空,则将P入栈并将P的左孩子置为当前的P,重复步骤1);若P为空,转至步骤2);

    (由此可知,步骤1)可由循环实现,且P为空为循环结束条件。下面称此循环为内循环)

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

    (由此可知,步骤2)为一条件分支,且栈非空为执行条件。下面称此为if语句)

      3)若P为NULL并且栈为空则遍历结束,转至步骤1);否则,结束循环。

    (由此可知,步骤3)为包含1)、2)的循环,且循环结束条件为P为NULL并且栈为空。下面称此循环为外循环)

    (注:3)中循环结束条件为P为NULL并且栈为空的原因:

    如果执行了内循环至少一次,栈就非空,那么结束左子树入栈,即内循环结束后,必然会进行对最后一个左子树的访问,即if语句必然会执行,if语句将当前结点P置为右子树,然后就要继续内循环以及if语句的操作;

    此处就需要一个循环结束条件,在栈中的元素没有右子树时,就不需要内循环;在栈为空时,就不需if语句;两个条件中的任意一个不满足时,就需再次进入外循环。

    因此,外循环的结束条件即为“P为NULL并且栈为空”。

    复制代码
    void inOrder2(BinTree *root)      //非递归中序遍历{
        stack<BinTree*> s;
        BinTree *p=root;
        while(p!=NULL||!s.empty())
        {
            while(p!=NULL)
            {
                s.push(p);
                p=p->lchild;
            }
            if(!s.empty())
            {
                p=s.top();
                cout<<p->data<<"";
                s.pop();
                p=p->rchild;
            }
        }    
    } 
    复制代码

      三.后序遍历

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

          1.递归实现

    复制代码
    void postOrder1(BinTree *root)    //递归后序遍历{
        if(root!=NULL)
        {
            postOrder1(root->lchild);
            postOrder1(root->rchild);
            cout<<root->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 postOrder3(BinTree *root)     //非递归后序遍历{
        stack<BinTree*> s;
        BinTree *cur;                      //当前结点     BinTree *pre=NULL;                 //前一次访问的结点     s.push(root);
        while(!s.empty())
        {
            cur=s.top();
            if((cur->lchild==NULL&&cur->rchild==NULL)||
               (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
            {
                cout<<cur->data<<"";  //如果当前结点没有孩子结点或者孩子节点都已被访问过               s.pop();
                pre=cur; 
            }
            else
            {
                if(cur->rchild!=NULL)
                    s.push(cur->rchild);
                if(cur->lchild!=NULL)    
                    s.push(cur->lchild);
            }
        }    
    }
    复制代码

     四.整个程序完整的代码

      1 /*二叉树的遍历* 2011.8.25*/ 
      2 
      3 #include <iostream>
      4 #include<string.h>
      5 #include<stack> 
      6 using namespace std;
      7 
      8 typedef struct node
      9 {
     10     char data;
     11     struct node *lchild,*rchild;
     12 }BinTree;
     13 
     14 typedef struct node1
     15 {
     16     BinTree *btnode;
     17     bool isFirst;
     18 }BTNode;
     19 
     20 
     21 void creatBinTree(char *s,BinTree *&root)  //创建二叉树,s为形如A(B,C(D,E))形式的字符串 
     22 {
     23     int i;
     24     bool isRight=false;
     25     stack<BinTree*> s1;          //存放结点 
     26     stack<char> s2;              //存放分隔符
     27     BinTree *p,*temp;
     28     root->data=s[0];
     29     root->lchild=NULL;
     30     root->rchild=NULL;
     31     s1.push(root);
     32     i=1;
     33     while(i<strlen(s))
     34     {
     35         if(s[i]=='(')
     36         {
     37             s2.push(s[i]);
     38             isRight=false;
     39         }    
     40         else if(s[i]==',')    
     41         {
     42             isRight=true;
     43         }
     44         else if(s[i]==')')
     45         {
     46             s1.pop();
     47             s2.pop();
     48         }
     49         else if(isalpha(s[i]))
     50         {
     51             p=(BinTree *)malloc(sizeof(BinTree));
     52             p->data=s[i];
     53             p->lchild=NULL;
     54             p->rchild=NULL;
     55             temp=s1.top();
     56             if(isRight==true)    
     57             {
     58                 temp->rchild=p;
     59                 cout<<temp->data<<"的右孩子是"<<s[i]<<endl;
     60             }
     61             else
     62             {
     63                 temp->lchild=p;
     64                 cout<<temp->data<<"的左孩子是"<<s[i]<<endl;
     65             }
     66             if(s[i+1]=='(')
     67                 s1.push(p);
     68         }
     69         i++;
     70     }    
     71 }
     72 
     73 void display(BinTree *root)        //显示树形结构 
     74 {
     75     if(root!=NULL)
     76     {
     77         cout<<root->data;
     78         if(root->lchild!=NULL)
     79         {
     80             cout<<'(';
     81             display(root->lchild);
     82         }
     83         if(root->rchild!=NULL)
     84         {
     85             cout<<',';
     86             display(root->rchild);
     87             cout<<')';
     88         }
     89     }
     90 }
     91 
     92 void preOrder1(BinTree *root)     //递归前序遍历 
     93 {
     94     if(root!=NULL)
     95     {
     96         cout<<root->data<<" ";
     97         preOrder1(root->lchild);
     98         preOrder1(root->rchild);
     99     }
    100 }
    101 
    102 void inOrder1(BinTree *root)      //递归中序遍历
    103 {
    104     if(root!=NULL)
    105     {
    106         inOrder1(root->lchild);
    107         cout<<root->data<<" ";
    108         inOrder1(root->rchild);
    109     }
    110 } 
    111 
    112 void postOrder1(BinTree *root)    //递归后序遍历
    113 {
    114     if(root!=NULL)
    115     {
    116         postOrder1(root->lchild);
    117         postOrder1(root->rchild);
    118         cout<<root->data<<" ";
    119     }    
    120 } 
    121 
    122 void preOrder2(BinTree *root)     //非递归前序遍历 
    123 {
    124     stack<BinTree*> s;
    125     BinTree *p=root;
    126     while(p!=NULL||!s.empty())
    127     {
    128         while(p!=NULL)
    129         {
    130             cout<<p->data<<" ";
    131             s.push(p);
    132             p=p->lchild;
    133         }
    134         if(!s.empty())
    135         {
    136             p=s.top();
    137             s.pop();
    138             p=p->rchild;
    139         }
    140     }
    141 }
    142 
    143 void inOrder2(BinTree *root)      //非递归中序遍历
    144 {
    145     stack<BinTree*> s;
    146     BinTree *p=root;
    147     while(p!=NULL||!s.empty())
    148     {
    149         while(p!=NULL)
    150         {
    151             s.push(p);
    152             p=p->lchild;
    153         }
    154         if(!s.empty())
    155         {
    156             p=s.top();
    157             cout<<p->data<<" ";
    158             s.pop();
    159             p=p->rchild;
    160         }
    161     }    
    162 } 
    163 
    164 void postOrder2(BinTree *root)    //非递归后序遍历
    165 {
    166     stack<BTNode*> s;
    167     BinTree *p=root;
    168     BTNode *temp;
    169     while(p!=NULL||!s.empty())
    170     {
    171         while(p!=NULL)              //沿左子树一直往下搜索,直至出现没有左子树的结点 
    172          {
    173             BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
    174             btn->btnode=p;
    175             btn->isFirst=true;
    176             s.push(btn);
    177             p=p->lchild;
    178         }
    179         if(!s.empty())
    180         {
    181             temp=s.top();
    182             s.pop();
    183             if(temp->isFirst==true)     //表示是第一次出现在栈顶 
    184              {
    185                 temp->isFirst=false;
    186                 s.push(temp);
    187                 p=temp->btnode->rchild;    
    188             }
    189             else                        //第二次出现在栈顶 
    190              {
    191                 cout<<temp->btnode->data<<" ";
    192                 p=NULL;
    193             }
    194         }
    195     }    
    196 } 
    197 
    198 void postOrder3(BinTree *root)     //非递归后序遍历
    199 {
    200     stack<BinTree*> s;
    201     BinTree *cur;                      //当前结点 
    202     BinTree *pre=NULL;                 //前一次访问的结点 
    203     s.push(root);
    204     while(!s.empty())
    205     {
    206         cur=s.top();
    207         if((cur->lchild==NULL&&cur->rchild==NULL)||
    208            (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
    209         {
    210             cout<<cur->data<<" ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
    211               s.pop();
    212             pre=cur; 
    213         }
    214         else
    215         {
    216             if(cur->rchild!=NULL)
    217                 s.push(cur->rchild);
    218             if(cur->lchild!=NULL)    
    219                 s.push(cur->lchild);
    220         }
    221     }    
    222 }
    223 
    224 
    225 int main(int argc, char *argv[])
    226 {
    227     char s[100];
    228     while(scanf("%s",s)==1)
    229     {
    230         BinTree *root=(BinTree *)malloc(sizeof(BinTree));
    231         creatBinTree(s,root);
    232         display(root);
    233         cout<<endl;
    234         preOrder2(root);
    235         cout<<endl; 
    236         inOrder2(root);
    237         cout<<endl;
    238         postOrder2(root);
    239         cout<<endl;
    240         postOrder3(root);
    241         cout<<endl;
    242     }
    243     return 0;
    244 }
    作者:海子

        

        

    本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    经典算法之猴子吃桃
    VS2008C#Sqlserver2008数据库的连接以及增删改查
    在数组中随机插入数字且不重复
    菲波那切数列
    Js之AJAX
    经典算法之冒泡排序
    《Head First 设计模式》 第一章 设计模式入门
    Redis 的 IO 多路复用,学习研究
    高性能MySQL 第十章 复制 Part2
    高性能MySQL 第十一章 可扩展的MySQL
  • 原文地址:https://www.cnblogs.com/youngforever/p/3104637.html
Copyright © 2011-2022 走看看