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

电玩女神 2022-01-26 15:11 392阅读 0赞

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

  1. 本文二叉树定义为:
  2. struct BinaryTreeNode
  3. {
  4. int m_nValue;
  5. BinaryTreeNode* m_pLeft;
  6. BinaryTreeNode* m_pRight;
  7. };

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

1.1前序遍历

  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中序遍历

  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后续遍历

  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习题举例

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

20130609184356000

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

20130609184418156

错误解法:

  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. }

正确解法:

  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. void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)
  15. {
  16. if(pNode == NULL)
  17. return;
  18. BinaryTreeNode *pCurrent = pNode;
  19. //visit_begin
  20. if (pCurrent->m_pLeft != NULL)
  21. ConvertNode(pCurrent->m_pLeft, pLastNodeInList);
  22. pCurrent->m_pLeft = *pLastNodeInList;
  23. if(*pLastNodeInList != NULL)
  24. (*pLastNodeInList)->m_pRight = pCurrent;
  25. *pLastNodeInList = pCurrent;
  26. //visit_end
  27. if (pCurrent->m_pRight != NULL)
  28. ConvertNode(pCurrent->m_pRight, pLastNodeInList);
  29. }

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

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

2.1前序遍历的非递归算法

  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中序遍历的非递归算法

  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后序遍历的非递归算法

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

发表评论

表情:
评论列表 (有 0 条评论,392人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    网上的递归遍历代码很多,这里就不赘述了,说一下思考的角度: 1. 把每一个棵子树都看成是独立的树; 2. 每一个节点都会把递归的代码重新执行一次; 3. 想象压栈的过程

    相关

    二叉树又称为红黑树,是一种常用的数据结构,而二叉树的遍历则是一种非常基本的操作。遍历二叉树的方式有两大类:递归和非递归。递归方式算法较为简便,并且更便于理解,非递归方式则需要对