二叉树三种非递归遍历的区别

  1 #include <iostream>
  2 
  3 #define MAXN  100
  4 using namespace std;
  5 
  6 
  7 struct BTNode
  8 {
  9     char tag;
 10     BTNode *left;
 11     BTNode *right;
 12 };
 13 
 14 class BTree
 15 {
 16 private:
 17     BTNode **root;
 18     void BuildBTree(BTNode **root);
 19 
 20 public:
 21     /*递归版本*/
 22     void PreVisit(BTNode *root);
 23     void InVisit(BTNode *root);
 24     void PostVisit(BTNode *root);
 25 
 26     /*非递归版本*/
 27     void NR_PreVisit(BTNode *root);
 28     void NR_InVisit(BTNode *root);
 29     void NR_PostVisit(BTNode *root);
 30 
 31     BTree(BTNode **r);
 32     BTree();
 33 };
 34 
 35 BTree::BTree()
 36 {
 37 
 38 }
 39 
 40 BTree::BTree(BTNode **r)
 41 {
 42     root = r;
 43     /*
 44     *root = new BTNode;
 45     (*root)->left = NULL;
 46     (*root)->right = NULL;
 47     */
 48     BuildBTree(root);
 49 }
 50 
 51 /*先序方式插入结点*/
 52 void BTree::BuildBTree(BTNode **root)
 53 {
 54     char c;
 55     
 56     c = getchar();
 57     if(c == '#') 
 58         *root=NULL;
 59     else{
 60         *root = new BTNode;
 61         (*root)->tag = c;
 62         BuildBTree(&(*root)->left);
 63         BuildBTree(&(*root)->right);
 64     }
 65 }
 66 
 67 void BTree::PreVisit(BTNode *root)
 68 {
 69     if(root!=NULL)
 70     {
 71         printf("%c ", root->tag );
 72         PreVisit(root->left);
 73         PreVisit(root->right);
 74     }
 75 }
 76 
 77 void BTree::InVisit(BTNode *root)
 78 {
 79     if(root!=NULL)
 80     {
 81         InVisit(root->left);
 82         printf("%c ", root->tag );
 83         InVisit(root->right);
 84     }
 85 }
 86 
 87 void BTree::PostVisit(BTNode *root)
 88 {
 89     if(root!=NULL)
 90     {
 91         PostVisit(root->left);
 92         PostVisit(root->right);
 93         printf("%c ", root->tag );
 94     }
 95 }
 96 
 97 void BTree::NR_PreVisit(BTNode *root)
 98 {
 99     BTNode *s[MAXN];
100     int top=0;
101 
102     while(top!=0 || root!=NULL)
103     {
104         while(root!=NULL)
105         {
106             s[top] = root;
107             printf("%c ", s[top++]->tag);
108             root = root->left;
109         }
110         if(top>0)
111         {
112             root = s[--top];
113             root = root->right;
114         }
115     }
116 }
117 
118 void BTree::NR_InVisit(BTNode *root)
119 {
120     BTNode *s[MAXN];
121     int top=0;
122     
123     while(top!=0 || root!=NULL)
124     {
125         while(root!=NULL)
126         {
127             s[top++]=root;
128             root = root->left;
129         }
130         if(top>0)
131         {
132             root = s[--top];
133             printf("%c ", root->tag);
134             root = root->right;
135         }
136     }
137 }
138 
139 void BTree::NR_PostVisit(BTNode *root)
140 {
141     BTNode *s[MAXN], *tmp=NULL;
142     int top=0;
143 
144     while(top!=0 || root!=NULL)
145     {
146         while(root!=NULL)
147         {
148             s[top++]=root;
149             root=root->left;
150         }
151         if(top>0)
152         {
153             root = s[--top];
154 
155             /*右孩子不存在或者已经访问过,root出栈并访问*/
156             if( (root->right == NULL) || (root->right == tmp) )  
157             {
158                 printf("%c ", root->tag);
159                 tmp = root;        //保存root指针
160                 root=NULL;         //当前指针置空,防止再次入栈
161             }
162 
163             /*不出栈,继续访问右孩子*/
164             else
165             {
166                 top++;             //与root=s[--top]保持平衡
167                 root = root->right;
168             }
169         }
170     }
171 }
172 
173 int main()
174 {
175     BTNode *root=NULL;
176     BTree bt(&root);  //头指针的地址
177     
178     bt.NR_PreVisit(root);
179     printf("\n");
180     bt.NR_InVisit(root);
181     printf("\n");
182     bt.NR_PostVisit(root);
183     printf("\n");
184     return 0;
185 }

先上代码,带NR(Non-recursive)的表示非递归遍历。

 

测试数据:

124#8##5##369###7##

 

表示的二叉树:

用windows自带的画图画的,的确是粗糙了点。。。

 

测试结果:

1 2 4 8 5 3 6 9 7
4 8 2 5 1 9 6 3 7
8 4 5 2 9 6 7 3 1

 

 

一、关于二叉树的建立

 

  首先要注意二叉树的创建过程,这里用的是先序方式递归插入结点,所以输入数据的时候,必须按照先序方式输入,

左结点或右结点为空的,用#表示。否则,输入不会有响应,因为递归过程还未结束,按CTRL+Z也没用。当然可以用其

他方式插入(如中序递归插入,后序递归插入等)。

 

二、三种非递归遍历的区别

 

  前序、中序和后序的递归遍历方式比较简单,这里就不讲了。而非递归的遍历方式,只需要用数组存储结点指针,模拟系统栈的工作机制就可以了。

先说先序非递归遍历,按照根-左-右的方式访问的话,需要将当前结点压栈(同时打印当前结点信息),直到左子树为空(内层while);然后出栈,访问

右结点;后面的操作就跟前面的一样了(外层while)。

  对于中序非递归遍历,可以看到代码结构几乎一模一样,只是打印结点信息的位置不同而已。这是因为中序遍历是左-根-右,这样前序和中序非

递归遍历(根-左和左-根都是压栈动作,且出栈动作的对象都是父节点)是一致的。

 

  对于后序非递归遍历,因为后序遍历是左-右-根,根的访问是在右孩子之后,而这意味着两种情况:

  1、右孩子不为空(继续访问右孩子);

  2、右孩子为空,从左孩子返回,则需要访问根结点。

  为了区分这两种情况(物理形式上从左孩子返回,还是从右孩子返回来访问根节点),对于右孩子的访问又需要判断根结点的右孩子是否为空或者已

访问过(右子树已遍历过)。除这两种情况外,都不应该访问根节点,而是要继续进入右子树。

  

三、补充说明

 

  在后序非递归遍历的else语句中top++纯粹是为了使栈保持平衡,因为对于2)继续访问右孩子这种情况,不需要出栈,而前面的root[--top]包含

出栈操作,以此保证栈的正确性(当然可以有其他的处理,这里也是考虑到三种非递归遍历方式的统一性)。

  两个while不会提高程序的时间复杂度,因为二叉树的结点个数是固定的,内层while是为了提高算法的逻辑性。

 

四、递归->非递归

 

  另外,今天实习看到一个老师写的非递归代码,非常棒,赞一个!他仅仅是将程序的返回地址和函数的形参、局部变量都保存起来,然后在退出时

还原现场;同样是非递归,但是这种方式更接近编译器的处理方式,同操作系统的任务切换也比较一致;所以这种处理方法为递归自动转换为非递归奠

定了基础。

  分享一下他当场编写的非递归的汉诺塔:

 

  1 #include <stdio.h>
  2 #include <iostream>
  3 
  4 using namespace std ;
  5 
  6 #define  MAXSIZE  1000 
  7 
  8 struct SNode
  9 {
 10     int  n;
 11     char from ;
 12     char to;
 13     char aux ;
 14     int  label ;
 15 } ;
 16 
 17 struct STK
 18 {
 19     
 20     SNode  stack[MAXSIZE] ;
 21     int sp  ;
 22     STK()
 23     {
 24         sp = 0 ;
 25     };
 26     void push (int n,char from,char to,char aux, int label )
 27     {
 28         if ( sp>= MAXSIZE )
 29         {
 30             printf ( "STK is full!\n" ) ;
 31         }
 32         stack[sp].n = n ;
 33         stack[sp].from = from ;
 34         stack[sp].to = to ;
 35         stack[sp].aux = aux ;
 36         stack[sp++].label = label ;
 37     };
 38     SNode POP()
 39     {
 40         if ( sp <=0 )
 41         {
 42             printf ( "STK is empty!\n" ) ;
 43         }
 44         return stack[--sp] ;
 45     };
 46 } ;
 47 
 48 void move(int n,char from,char to,char aux)
 49 {
 50     if(n==1)
 51     {
 52         cout<<"将#1盘从"<<from<<"移到"<<to<<endl;
 53 }
 54     else
 55     {
 56          move(n-1,from,aux,to);
 57          cout<<"将#"<<n<<"盘从"<<from<<"移到"<<to<<endl;
 58          move(n-1,aux,to,from);
 59 }
 60 }
 61 
 62 
 63 void move_stk(int n,char from,char to,char aux)
 64 {
 65     STK stk ;
 66     char tmp;
 67 S1:
 68     if(n==1)
 69     {
 70         cout<<"将#1盘从"<<from<<"移到"<<to<<endl;
 71     }
 72     else
 73    {
 74     stk.push (n,from,to,aux,2 ) ;
 75     n = n-1 ;
 76     tmp = to ;
 77     to = aux ;  
 78     aux = tmp ;
 79     goto S1;
 80          // move(n-1,from,aux,to);
 81 S2:
 82          cout<<"将#"<<n<<"盘从"<<from<<"移到"<<to<<endl;
 83 
 84     stk.push (n,from,to,aux,3 ) ;
 85     n = n-1 ;
 86     tmp = from ;
 87     from = aux ;  
 88     aux = tmp ;
 89     goto S1;
 90          // move(n-1,aux,to,from);
 91 }
 92 S3:
 93     if ( stk.sp > 0 )
 94     {
 95         SNode sn = stk.POP() ;
 96         n = sn.n ;
 97         from = sn.from;
 98         to = sn.to ;
 99         aux = sn.aux ;
100         if  ( 1 == sn.label  )
101             goto S1;
102         else if ( 2 == sn.label )
103             goto S2;
104         else 
105             goto S3;        
106     }
107 }
108 
109 
110 
111 int main(int argc, char * argv[])
112 {
113     move ( 3,'A','B', 'C' );
114     printf ( "================================\n" ) ;
115     move_stk ( 3,'A','B', 'C' );
116 
117     return 0;
118 }

 

 

posted on 2012-07-05 15:21  Seiyagoo  阅读(1552)  评论(5编辑  收藏  举报