C++编程练习(17)----“二叉树非递归遍历的实现“

二叉树的非递归遍历

最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试。

C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历、中序遍历、后续遍历)

递归的思想也就是栈的思想,既然不用递归,那就改用栈的方式。



“递归=栈”



1、前序遍历

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

a)递归实现前序遍历:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void PreOrderTraverse(BiTNode *T)   /*递归前序遍历*/  
  2. {  
  3.     if (T==NULL)  
  4.         return;  
  5.     std::cout<<T->data<<"\t";  
  6.     PreOrderTraverse(T->lchild);  
  7.     PreOrderTraverse(T->rchild);  
  8. }  

b)非递归实现前序遍历:

对于根结点P:
1)访问结点P,并将结点P入栈;
2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;
3)直到P为NULL并且栈为空,则遍历结束。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void nPreOrderTraverse(BiTNode *T)  /*非递归前序遍历*/  
  2. {  
  3.     if (T==NULL)  
  4.         return;  
  5.     BiTNode *p;  
  6.     p = T;  
  7.     std::stack<BiTNode*> stk;  
  8.     while(p!=NULL||!stk.empty())  
  9.     {  
  10.         while(p!=NULL)  
  11.         {  
  12.             std::cout<<p->data<<"\t";  
  13.             stk.push(p);  
  14.             p = p->lchild;  
  15.         }  
  16.         if(!stk.empty())  
  17.         {  
  18.             p = stk.top();  
  19.             stk.pop();  
  20.             p = p->rchild;  
  21.         }  
  22.     }     
  23. }  

2、中序遍历

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

a)递归实现中序遍历

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void InOrderTraverse(BiTNode *T)    /*递归中序遍历*/  
  2. {  
  3.     if (T==NULL)  
  4.         return;  
  5.     InOrderTraverse(T->lchild);  
  6.     std::cout<<T->data<<"\t";  
  7.     InOrderTraverse(T->rchild);  
  8. }  

b)非递归实现中序遍历

对于根结点P:
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3)直到P为NULL并且栈为空则遍历结束。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void nInOrderTraverse(BiTNode *T)   /*非递归中序遍历*/  
  2. {  
  3.     if(T==NULL)  
  4.         return;  
  5.     std::stack<BiTNode*> stk;  
  6.     BiTNode* p;  
  7.     p = T;  
  8.     while(p!=NULL || !stk.empty())  
  9.     {  
  10.         while(p!=NULL)  
  11.         {  
  12.             stk.push(p);  
  13.             p = p->lchild;  
  14.         }  
  15.         if(!stk.empty())  
  16.         {  
  17.             p = stk.top();  
  18.             stk.pop();  
  19.             std::cout<<p->data<<"\t";  
  20.             p = p->rchild;  
  21.         }  
  22.     }  
  23. }  

3、后序遍历

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

a)递归实现后序遍历

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void PostOrderTraverse(BiTNode *T)  /*递归后序遍历*/  
  2. {  
  3.     if(T==NULL)  
  4.             return;  
  5.     PostOrderTraverse(T->lchild);  
  6.     PostOrderTraverse(T->rchild);  
  7.     std::cout<<T->data<<"\t";  
  8. }  

b)非递归实现后序遍历

这里实现略复杂,当初想出来的方法过于笨重,后参考http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html,改写如下:

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

对于根结点P:

1)将P入栈,设置当前结点 cur 

2)将当前的 cur 置为栈顶结点,如果 cur 不存在左孩子和右孩子,或者 cur 存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则可以直接访问该结点并进行出栈操作。否则将 cur 的右孩子和左孩子依次入栈;

3)直到栈为空则遍历结束。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void nPostOrderTraverse(BiTNode *T)     /*非递归后序遍历*/  
  2. {  
  3.     if(T==NULL)  
  4.         return;  
  5.     BiTNode* cur;       /*当前结点*/  
  6.     BiTNode* pre = NULL;        /*前一次输出的结点*/  
  7.     std::stack<BiTNode*> stk;  
  8.     stk.push(T);  
  9.     while(!stk.empty())  
  10.     {  
  11.         cur = stk.top();  
  12.         if((cur->lchild==NULL && cur->rchild==NULL) ||  
  13.             (pre!=NULL && (cur->lchild==pre || cur->rchild==pre)))      
  14.         {                               /*如果当前结点没有孩子结点或者孩子节点都已被访问过*/  
  15.             std::cout<<cur->data<<"\t";  
  16.             stk.pop();  
  17.             pre = cur;  
  18.         }  
  19.         else  
  20.         {  
  21.             if(cur->rchild!=NULL)  
  22.                 stk.push(cur->rchild);  
  23.             if(cur->lchild!=NULL)  
  24.                 stk.push(cur->lchild);  
  25.         }  
  26.     }  
  27. }  

4、完整测试代码

1)BiTree.h头文件

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* BiTree.h头文件 */  
  2. #include<iostream>  
  3. #include<stack>  
  4. typedef char TElemType;  
  5.   
  6. class BiTNode{                  /*创建结点类,使用的是左右孩子表示法*/  
  7. public:  
  8.     BiTNode():data(0),lchild(NULL),rchild(NULL){}  
  9.     TElemType data;  
  10.     BiTNode *lchild,*rchild;  
  11. };  
  12.   
  13. void CreateBiTree(BiTNode **T)      /*二叉树的建立,这里形参用的是双指针,需要注意*/  
  14. {                                           /*这里输入的是一个扩展二叉树,每个结点若有空指针,*/  
  15.     TElemType ch;                               /*则将其值设为一个特定值,本代码中是'#'*/  
  16.     std::cin>>ch;  
  17.     std::cin.clear();  
  18.     if(ch=='#')  
  19.         *T=NULL;  
  20.     else  
  21.     {  
  22.         *T=new BiTNode;  
  23.         if(!*T)  
  24.             exit(1);  
  25.         (*T)->data=ch;  
  26.         CreateBiTree(&(*T)->lchild);  
  27.         CreateBiTree(&(*T)->rchild);  
  28.     }  
  29. }  
  30.   
  31. void PreOrderTraverse(BiTNode *T)   /*递归前序遍历*/  
  32. {  
  33.     if (T==NULL)  
  34.         return;  
  35.     std::cout<<T->data<<"\t";  
  36.     PreOrderTraverse(T->lchild);  
  37.     PreOrderTraverse(T->rchild);  
  38. }  
  39.   
  40. void nPreOrderTraverse(BiTNode *T)  /*非递归前序遍历*/  
  41. {  
  42.     if (T==NULL)  
  43.         return;  
  44.     BiTNode *p;  
  45.     p = T;  
  46.     std::stack<BiTNode*> stk;  
  47.     while(p!=NULL||!stk.empty())  
  48.     {  
  49.         while(p!=NULL)  
  50.         {  
  51.             std::cout<<p->data<<"\t";  
  52.             stk.push(p);  
  53.             p = p->lchild;  
  54.         }  
  55.         if(!stk.empty())  
  56.         {  
  57.             p = stk.top();  
  58.             stk.pop();  
  59.             p = p->rchild;  
  60.         }  
  61.     }     
  62. }  
  63.   
  64.   
  65. void InOrderTraverse(BiTNode *T)    /*递归中序遍历*/  
  66. {  
  67.     if (T==NULL)  
  68.         return;  
  69.     InOrderTraverse(T->lchild);  
  70.     std::cout<<T->data<<"\t";  
  71.     InOrderTraverse(T->rchild);  
  72. }  
  73.   
  74. void nInOrderTraverse(BiTNode *T)   /*非递归中序遍历*/  
  75. {  
  76.     if(T==NULL)  
  77.         return;  
  78.     std::stack<BiTNode*> stk;  
  79.     BiTNode* p;  
  80.     p = T;  
  81.     while(p!=NULL || !stk.empty())  
  82.     {  
  83.         while(p!=NULL)  
  84.         {  
  85.             stk.push(p);  
  86.             p = p->lchild;  
  87.         }  
  88.         if(!stk.empty())  
  89.         {  
  90.             p = stk.top();  
  91.             stk.pop();  
  92.             std::cout<<p->data<<"\t";  
  93.             p = p->rchild;  
  94.         }  
  95.     }  
  96. }  
  97.   
  98. void PostOrderTraverse(BiTNode *T)  /*递归后序遍历*/  
  99. {  
  100.     if(T==NULL)  
  101.             return;  
  102.     PostOrderTraverse(T->lchild);  
  103.     PostOrderTraverse(T->rchild);  
  104.     std::cout<<T->data<<"\t";  
  105. }  
  106.   
  107. void nPostOrderTraverse(BiTNode *T)     /*非递归后序遍历*/  
  108. {  
  109.     if(T==NULL)  
  110.         return;  
  111.     BiTNode* cur;       /*当前结点*/  
  112.     BiTNode* pre = NULL;        /*前一次输出的结点*/  
  113.     std::stack<BiTNode*> stk;  
  114.     stk.push(T);  
  115.     while(!stk.empty())  
  116.     {  
  117.         cur = stk.top();  
  118.         if((cur->lchild==NULL && cur->rchild==NULL) ||  
  119.             (pre!=NULL && (cur->lchild==pre || cur->rchild==pre)))      
  120.         {                               /*如果当前结点没有孩子结点或者孩子节点都已被访问过*/  
  121.             std::cout<<cur->data<<"\t";  
  122.             stk.pop();  
  123.             pre = cur;  
  124.         }  
  125.         else  
  126.         {  
  127.             if(cur->rchild!=NULL)  
  128.                 stk.push(cur->rchild);  
  129.             if(cur->lchild!=NULL)  
  130.                 stk.push(cur->lchild);  
  131.         }  
  132.     }  
  133. }  

2)main文件

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include"BiTree.h"  
  2. using namespace std;  
  3. int main()  
  4. {  
  5.     BiTNode *T=new BiTNode;  
  6.     std::cout<<"请前序遍历输入各节点:";  
  7.     CreateBiTree(&T);  
  8.     cout<<"\n该树的前序遍历输出为:"<<endl;  
  9.     PreOrderTraverse(T);  
  10.     cout<<endl;  
  11.     nPreOrderTraverse(T);  
  12.     cout<<"\n该树的中序遍历输出为:"<<endl;  
  13.     InOrderTraverse(T);  
  14.     cout<<endl;  
  15.     nInOrderTraverse(T);  
  16.     cout<<"\n该树的后序遍历输出为:"<<endl;  
  17.     PostOrderTraverse(T);  
  18.     cout<<endl;  
  19.     nPostOrderTraverse(T);  
  20.     cout<<endl;  
  21.     return 0;  
  22. }  

5、测试结果







最后,用表格罗列一下前序、中序和后续遍历编程实现的区别,以便加强记忆。

 

不同点

相同点

前序遍历

1)在最外侧左子节点压入栈中的同时,输出该子节点的值;

2)发现节点存在“右节点”时,需要将当前节点弹出;

第一步,以根节点为起始,将左右最外侧左子节点压入栈中;

第二步,从栈顶元素进行判别,看是否存在右子节点;

第三步,重复第一、第二步,知道栈中元素为空

中序遍历

1)在最外侧左子节点压入栈中的时候,并不进行节点输出;

2)发现节点存在“右节点”时,需要将当前节点弹出;

后序遍历

1)在最外侧左子节点压入栈中的时候,并不进行节点输出;

2)发现节点存在“右节点”时,不需要弹出当前节点;

【注意】:第二次遍历到“既含有左节点又含有右节点”的根节点时,需要弹出该节点。因此需要利用两个堆栈来记录重复遍历的节点。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值