zoukankan      html  css  js  c++  java
  • 【经典面试题二】二叉树的递归与非递归遍历(前序、中序、后序)

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

    一.前序遍历

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

       1.递归实现

    复制代码
    1 void preOrder1(BinTree *root)     //递归前序遍历 
    2 {
    3     if(root!=NULL)
    4     {
    5         cout<<root->data<<" ";
    6         preOrder1(root->lchild);
    7         preOrder1(root->rchild);
    8     }
    9 }
    复制代码

       2.非递归实现

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

      对于任一结点P:

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

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

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

    复制代码
     1 void preOrder2(BinTree *root)     //非递归前序遍历 
     2 {
     3     stack<BinTree*> s;
     4     BinTree *p=root;
     5     while(p!=NULL||!s.empty())
     6     {
     7         while(p!=NULL)
     8         {
     9             cout<<p->data<<" ";
    10             s.push(p);
    11             p=p->lchild;
    12         }
    13         if(!s.empty())
    14         {
    15             p=s.top();
    16             s.pop();
    17             p=p->rchild;
    18         }
    19     }
    20 }
    复制代码

    二.中序遍历

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

      1.递归实现

    复制代码
    1 void inOrder1(BinTree *root)      //递归中序遍历
    2 {
    3     if(root!=NULL)
    4     {
    5         inOrder1(root->lchild);
    6         cout<<root->data<<" ";
    7         inOrder1(root->rchild);
    8     }
    9 }
    复制代码

      2.非递归实现

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

      对于任一结点P,

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

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

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

    复制代码
     1 void inOrder2(BinTree *root)      //非递归中序遍历
     2 {
     3     stack<BinTree*> s;
     4     BinTree *p=root;
     5     while(p!=NULL||!s.empty())
     6     {
     7         while(p!=NULL)
     8         {
     9             s.push(p);
    10             p=p->lchild;
    11         }
    12         if(!s.empty())
    13         {
    14             p=s.top();
    15             cout<<p->data<<" ";
    16             s.pop();
    17             p=p->rchild;
    18         }
    19     }    
    20 } 
    复制代码

    三.后序遍历

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

           1.递归实现

    复制代码
    1 void postOrder1(BinTree *root)    //递归后序遍历
    2 {
    3     if(root!=NULL)
    4     {
    5         postOrder1(root->lchild);
    6         postOrder1(root->rchild);
    7         cout<<root->data<<" ";
    8     }    
    9 }
    复制代码

      2.非递归实现

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

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

    复制代码
     1 void postOrder2(BinTree *root)    //非递归后序遍历
     2 {
     3     stack<BTNode*> s;
     4     BinTree *p=root;
     5     BTNode *temp;
     6     while(p!=NULL||!s.empty())
     7     {
     8         while(p!=NULL)              //沿左子树一直往下搜索,直至出现没有左子树的结点 
     9         {
    10             BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
    11             btn->btnode=p;
    12             btn->isFirst=true;
    13             s.push(btn);
    14             p=p->lchild;
    15         }
    16         if(!s.empty())
    17         {
    18             temp=s.top();
    19             s.pop();
    20             if(temp->isFirst==true)     //表示是第一次出现在栈顶 
    21              {
    22                 temp->isFirst=false;
    23                 s.push(temp);
    24                 p=temp->btnode->rchild;    
    25             }
    26             else                        //第二次出现在栈顶 
    27              {
    28                 cout<<temp->btnode->data<<" ";
    29                 p=NULL;
    30             }
    31         }
    32     }    
    33 }
    复制代码

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

    复制代码
     1 void postOrder3(BinTree *root)     //非递归后序遍历
     2 {
     3     stack<BinTree*> s;
     4     BinTree *cur;                      //当前结点 
     5     BinTree *pre=NULL;                 //前一次访问的结点 
     6     s.push(root);
     7     while(!s.empty())
     8     {
     9         cur=s.top();
    10         if((cur->lchild==NULL&&cur->rchild==NULL)||
    11            (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
    12         {
    13             cout<<cur->data<<" ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
    14               s.pop();
    15             pre=cur; 
    16         }
    17         else
    18         {
    19             if(cur->rchild!=NULL)
    20                 s.push(cur->rchild);
    21             if(cur->lchild!=NULL)    
    22                 s.push(cur->lchild);
    23         }
    24     }    
    25 }
    复制代码

    四.整个程序完整的代码

    复制代码
      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 }
     
  • 相关阅读:
    激光打印机基于主机驱动程序、基于 PCL 驱动程序和 PostScript 驱动程序有何区别?
    转贴:打印机语言PostScript和PCL的比较
    编译器相关配置简介
    Graphics View的体系结构
    解决重装Qt后不能编译Examples的问题
    有符号数和无符号数的区别
    51单片机的外设
    AT89S52单片机P3口解惑
    双向端口设计
    AT89s52单片机的掉电测试
  • 原文地址:https://www.cnblogs.com/TX980502/p/7657111.html
Copyright © 2011-2022 走看看