二叉树遍历(递归与迭代) C++

转载:http://blog.csdn.net/sicofield/article/details/9066987

二叉树遍历算法分为前序(PreOredr),中序(InOrder),后序(PostOrder)遍历。并且可以设计递归型或者迭代型算法。

        本文二叉树定义为:

[cpp]  view plain copy
  1. struct BinaryTreeNode  
  2. {  
  3.     int m_nValue;  
  4.     BinaryTreeNode* m_pLeft;  
  5.     BinaryTreeNode* m_pRight;  
  6. };  

1.二叉树的递归遍历算法

1.1前序遍历
[cpp]  view plain copy
  1. void PreOrder(BinaryTreeNode* pRoot)  
  2. {  
  3.     if (pRoot!=NULL)  
  4.     {  
  5.         visit(*pRoot);//visit只是代表处理,关键的是结构  
  6.         PreOrder(pRoot->m_pLeft);  
  7.         PreOrder(pRoot->m_pRight);  
  8.     }     
  9. }  
1.2中序遍历
[cpp]  view plain copy
  1. void InOrder(BinaryTreeNode* pRoot)  
  2. {  
  3.     if (pRoot!=NULL)  
  4.     {  
  5.         InOrder(pRoot->m_pLeft);  
  6.         visit(*pRoot); //visit只是代表处理,关键的是结构  
  7.         InOrder(pRoot->m_pRight);  
  8.     }     
  9. }  
1.3后续遍历
[cpp]  view plain copy
  1. void PostOrder(BinaryTreeNode* pRoot)  
  2. {  
  3.     if (pRoot!=NULL)  
  4.     {  
  5.         PostOrder(pRoot->m_pLeft);  
  6.         PostOrder(pRoot->m_pRight);  
  7.         visit(*pRoot);//visit只是代表处理,关键的是结构  
  8.     }     
  9. }  

二叉树的三种遍历代码都很简洁,但是这里只是描述结构。根据相应的应用进行设计的时候按照这个结构写递归算法就可以了。关于递归算法顺便提一句很浅显但是设计算法的时候易忽略的一点。二叉树的递归肯定是有底层的叶节点向根节点进行的。

1.4习题举例

       输入一棵二叉搜索树,将该二叉搜索树转换为一个排序的双向链表。要求不能创建任何新的结点,只能调整树中指针的指向。


分析:因为是二叉搜索树所以转变的过程就是节点的左指针指向左子树的最大值,又指针指向右子树的最小值。可以用中序遍历的算法结构来编程。


错误解法:

[cpp]  view plain copy
  1. BinaryTreeNode* findMin(BinaryTreeNode* pNode)  
  2. {  
  3.     if (pNode==NULL)  
  4.         return NULL;  
  5.     BinaryTreeNode* min=pNode;  
  6.     while (min->m_pLeft!=NULL)  
  7.         min=min->m_pLeft;  
  8.     return min;  
  9. }  
  10. BinaryTreeNode* findMax(BinaryTreeNode* pNode)  
  11. {  
  12.     if (pNode==NULL)  
  13.         return NULL;  
  14.     BinaryTreeNode* max=pNode;  
  15.     while (max->m_pRight!=NULL)  
  16.         max=max->m_pRight;  
  17.     return max;  
  18. }  
  19. //从本质上看,这个根本就不是一个二叉树递归算法  
  20. void ConvertNode(BinaryTreeNode* pNode)  
  21. {  
  22.     if (pNode==NULL)  
  23.         return;  
  24.     BinaryTreeNode* leftNode=pNode->m_pLeft;  
  25.     BinaryTreeNode* rightNode=pNode->m_pRight;//保存下次迭代的值  
  26.     pNode->m_pLeft=findMax(leftNode);  
  27.     //反向的指针如何处理?如果对叶节点也设置反向的指针的话,那么就是一个无限循环  
  28.     pNode->m_pRight=findMin(rightNode);  
  29.     ConvertNode(leftNode);  
  30.     ConvertNode(rightNode);  
  31. }  
  32. BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)  
  33. {  
  34.     if(pRootOfTree==NULL)  
  35.         return NULL;  
  36.     BinaryTreeNode* pFirstOfList=findMin(pRootOfTree);//直接找到最小值  
  37.     ConvertNode(pRootOfTree);  
  38.     return pFirstOfList;  
  39. }  

正确解法:

[cpp]  view plain copy
  1. BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)  
  2. {  
  3. if(pRootOfTree==NULL)  
  4.     return NULL;  
  5.     BinaryTreeNode *pLastNodeInList = NULL;  
  6.     ConvertNode(pRootOfTree, &pLastNodeInList);  
  7.     // pLastNodeInList指向双向链表的尾结点,  
  8.     // 我们需要返回头结点  
  9.     BinaryTreeNode *pHeadOfList = pLastNodeInList;  
  10.     while(pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)  
  11.         pHeadOfList = pHeadOfList->m_pLeft;  
  12.     return pHeadOfList;  
  13. }  
  14.   
  15. void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)  
  16. {  
  17.     if(pNode == NULL)  
  18.         return;  
  19.     BinaryTreeNode *pCurrent = pNode;  
  20.     //visit_begin  
  21.     if (pCurrent->m_pLeft != NULL)  
  22.         ConvertNode(pCurrent->m_pLeft, pLastNodeInList);  
  23.     pCurrent->m_pLeft = *pLastNodeInList;   
  24.     if(*pLastNodeInList != NULL)  
  25.         (*pLastNodeInList)->m_pRight = pCurrent;  
  26.     *pLastNodeInList = pCurrent;  
  27.     //visit_end  
  28.     if (pCurrent->m_pRight != NULL)  
  29.         ConvertNode(pCurrent->m_pRight, pLastNodeInList);  
  30. }  

2.二叉树的迭代遍历算法

为了把一个递归过程改为非递归过程,就需要自己维护一个辅助栈结构,记录遍历时的回退路径。非递归的快速排序的设计依据也是这个。

2.1前序遍历的非递归算法
[cpp]  view plain copy
  1. #include<stack>  
  2. void PreOrder(BinaryTreeNode* pRoot)  
  3. {  
  4.     if (pRoot==NULL)  
  5.         return;  
  6.     std::stack<BinaryTreeNode*> S;  
  7.     BinaryTreeNode *p=pRoot;   //二叉树分左右,所以光有栈不行,合理的运用遍历指针是关键之一  
  8.     while(p!=NULL)  
  9.     {  
  10.         visit(p);  
  11.         if (p->m_pRight!=NULL)  
  12.             S.push(p->m_pRight);  
  13.         if (p->m_pLeft!=NULL)  
  14.             p=p->m_pLeft;  
  15.         else  
  16.         {  
  17.             if (S.empty())  
  18.                 break;  
  19.             p=S.top();  
  20.             S.pop();  
  21.         }  
  22.     }  
  23. }  

2.2中序遍历的非递归算法
[cpp]  view plain copy
  1. #include<stack>  
  2. void InOrder(BinaryTreeNode* pRoot)  
  3. {  
  4.     if (pRoot==NULL)  
  5.         return;  
  6.     std::stack<BinaryTreeNode*> S;  
  7.     BinaryTreeNode *p=pRoot;  
  8.     do   
  9.     {  
  10.         while(p!=NULL)  
  11.         {  
  12.             S.push(p);  
  13.             p->m_pLeft;  
  14.         }  
  15.         //若进行到这里左子树为空  
  16.         if (!S.empty())//Stack不空时退栈,然后访问该元素  
  17.         {  
  18.             p=S.top();  
  19.             S.pop();  
  20.             visit(p);  
  21.             p=p->m_pRight;  
  22.         }  
  23.     } while (p!=NULL||!S.empty());  
  24.     //这里的p==NULL表示右子树为空,然后堆栈如果也空的话,才是处理完毕  
  25. }  

2.3后序遍历的非递归算法

        迭代后序遍历比较复杂。在遍历完左子树时还不能访问根节点,需要再遍历又子树。待右子树遍历完后才访问根节点。所以在辅助栈工作记录中必须注明节点是在左子树还是在右子树。

[cpp]  view plain copy
  1. //记住二叉树后序遍历的非递归调用需要用pair类型,因为需要记录是左进还是右进,所以每个节点必然会进栈两次!!!  
[cpp]  view plain copy
  1. void PostOrder(BinaryTreeNode* pRoot)  
  2. {  
  3.     if (pRoot==NULL)  
  4.         return;  
  5.     std::pair<BinaryTreeNode*,char> w;  
  6.     std::stack<std::pair<BinaryTreeNode*,char> > S;  
  7.     BinaryTreeNode *p=pRoot;        
  8.     do   
  9.     {  
  10.         while(p!=NULL)           //左子树经过节点加L进栈  
  11.         {  
  12.             w.first=p;  
  13.             w.second='L';  
  14.             S.push(w);  
  15.             p=p->m_pLeft;  
  16.         }  
  17.         bool continuel=true;     //继续循环标志,用于L改为R的时候就开始向右遍历  
  18.         while (continuel && !S.empty()) //用一个break语句也能实现循环标志continuel的功能  
  19.         {  
  20.             w=S.top();  
  21.             S.pop();  
  22.             p=w.first;  
  23.             if (w.second=='L')  //标记为L表示左子树遍历完  
  24.             {  
  25.                 w.second=='R';  
  26.                 S.push(w);  
  27.                 continuel=false;  
  28.                 p=p->m_pRight;  
  29.             }  
  30.             else  
  31.                 visit(p);      //如果标记为R,表示右子树遍历完  
  32.         }  
  33.     }while (!S.empty());  
  34. }  



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值