《二叉樹的遍歷》

一、二叉樹的遍歷:按照某種順序訪問二叉樹中的每個結點,並使每個結點被訪問一次且只被訪問一次。

二、訪問怎樣理解?
就是對結點的增加、刪除、查閱、修改或加工。(我們先簡化爲對結點數據域值的輸出)

三、遍歷的作用:將非線性結構變成線性結構

四、遍歷種類:
1、前序遍歷/先根(序)遍歷/先序遍歷
2、中序遍歷/中根(序)遍歷/中序遍歷
3、後序遍歷/後根(序)遍歷/後序遍歷
4、層次序遍歷

五、前序遍歷
1、遞歸算法:
template<class T>
void BinaryTree<T>::preOrder(BinTreeNode<T>* subTree, void(*visit)(BinTreeNode<T>* p))
{
       if (subTree != nullptr)
       {
              visit(subTree);
              preOrder(subTree->leftChild, visit);
              preOrder(subTree->rightChild, visit);
       }
}

2、非遞歸算法:
<1> 思路1:(如果二叉樹非空,則)
(1)將二叉樹的根結點作爲當前結點;
(2)若當前結點非空,則先訪問該節點,並將該結點進棧,再將其左孩子結點作爲當前結點,重複步驟(2),直到當前結點爲空爲止;
(3)若棧非空,則將棧頂結點出棧,並將當前結點的右孩子結點作爲當前結點;
(4)重複步驟(2)、(3),直到棧爲空且當前結點爲空爲止。

<2>實現1:
template <class T>
void BinaryTree<T>::preOrder(void(*visit)(BinTreeNode<T>*p))
{
       stack<BinTreeNode<T>*> s;
       BinTreeNode<T>* t = root;
       while (!s.empty() || t != 0)
       {
              while (t != nullptr)
              {
                     visit(t);
                     s.push(t);
                     t = t->leftChild;
              }
              if (!s.isEmpty())
              {
                     t = s.top();
                     s.pop();
                     t = t->rightChild;
              }
       }
}

<3>思路2:(如果二叉樹非空,則)
(1)將二叉樹的根結點進棧;
(2)若棧非空,則將棧頂結點出棧並對其實施訪問;
(3)若該結點有右子樹,則將右子樹的根結點進棧;
(4)若該結點有左子樹,則將左子樹的根結點進棧;
(5)重複步驟(2),(3),(4),直到棧爲空爲止。

<4>實現2:
template<class T>
void BinaryTree<T>::preOrder(void(*visit)(BinTreeNode<T>* p))
{
       stack<BinTreeNode<T>*> s;
       t = root;
       s.push(root);
       while (!s.empty())
       {
              t = s.top();
              s.pop();
              visit(t);
              if (t->rightChild != nullptr) s.push(t->rightChild);
              if (t->leftChild != nullptr) s.push(t->leftChild);
       }
}


六、中序遍歷
1、遞歸算法:
template<class T>
void BinaryTree<T>::inOrder(BinTreeNode<T>* subTree, void(*visit)(BinTreeNode<T>* p))
{
       if (subTree != nullptr)
       {
              inOrder(subTree->leftChild, visit);
              visit(subTree);
              inOrder(subTree->rightChild, visit);
       }
}

2、非遞歸算法:
<1>思路:(若二叉樹非空,則)
(1)將二叉樹的根結點作爲當前結點;
(2)若當前結點非空,則該結點進棧並將其左孩子結點作爲當前結點,重複步驟(2),直到當前結點爲空爲止;
(3)若棧非空,則將棧頂結點出棧並作爲當前結點,接着訪問當前結點,再將當前結點的右孩子結點作爲當前結點;
(4)重複步驟(2),(3),直到棧爲空且當前結點爲空爲止。
<2>實現:
template<class T>
void BinaryTree<T>::inOrder(void(*visit)(BinTreeNode<T>* p))
{
       stack<BinTreeNode<T>*> s;
       BinTreeNode<T>* t = root;
       while (!s.empty()||t!=nullptr)
       {
              while (t != nullptr)
              {
                     s.push(t);
                     t = t->leftChild;
              }
              if (!s.empty())
              {
                     t = s.top();
                     s.pop();
                     visit(t);
                     t = t->rightChild;
              }
       }
}

七、後序遍歷
1、遞歸算法:
template<class T>
void BinaryTree<T>::postOrder(BinTreeNode<T>* subTree, void(*visit)(BinTreeNode<T>* p))
{
       if (subTree != nullptr)
       {
              postOrder(subTree->leftChild, visit);
              postOrder(subTree->rightChild, visit);
              visit(subTree);
       }
}

2、非遞歸算法:
<1>思路:每個結點需要進棧,出棧各兩次。爲了區別同一個結點的兩次進棧,需要設置一個標誌flag。
flag = 0,第一次進棧,表示該結點出棧後不能訪問;
flag = 1,第二次進棧,表示該結點出棧後可以訪問。

<2>實現:
template<class T>
void BinaryTree<T>::postOrder(void(*visit)(BinTreeNode<T>* p))
{
       int flag;
       stack<BinTreeNode<T>*> s1;
       stack<int> s2;
       BinTreeNode<T>* t = root;
       while (!s1.empty() || t != nullptr)
       {
              while (t != nullptr)//"1"
              {
                     s1.push(t);
                     s2.push(0);
                     t = t->leftChild;
              }
              if (!s.empty())
              {
                     t = s1.top();
                     s1.pop();
                     flag = s2.top();
                     s2.pop();
                     if (flag == 1)
                     {
                           visit(t);
                           t = nullptr; //目的:跳過“1”
                     }
                     else
                     {
                           s1.push(t);
                           s2.push(1);
                           t = t->rightChild;
                     }
              }
       }
}

八、以上三種非遞歸遍歷算法的統一
1、思路:一般從代碼實現最複雜的非遞歸遍歷算法入手,然後加以改動,即對後序遍歷的非遞歸算法進行改動即可統一三種非遞歸算法;
2、實現:(只需改動後序遍歷的非遞歸算法代碼中語句visit(t)的位置)
template<class T>
void BinaryTree<T>::postOrder(void(*visit)(BinTreeNode<T>* p))
{
       int flag;
       stack<BinTreeNode<T>*> s1;
       stack<int> s2;
       BinTreeNode<T>* t = root;
       while (!s1.empty() || t != nullptr)
       {
              while (t != nullptr)//"1"
              {
                     //visit(t);(前序遍歷)
                     s1.push(t);
                     s2.push(0);
                     t = t->leftChild;
              }
              if (!s.empty())
              {
                     t = s1.top();
                     s1.pop();
                     flag = s2.top();
                     s2.pop();
                     if (flag == 1)
                     {
                           //visit(t);(後序遍歷)
                           t = nullptr; //跳過“1”
                     }
                     else
                     {
                           //visit(t);(中序遍歷)
                           s1.push(t);
                           s2.push(1);
                           t = t->rightChild;
                     }
              }
       }
}

九、層次序遍歷
1、思路:
(1)遍歷之前先將二叉樹的根結點存入隊列中;
(2)然後依次從隊列中取出隊頭結點,每取出一個結點,都先訪問該結點;
(3)接着分別檢查該結點是否存在左、右孩子,若存在,則先後入列;
(4)如此反覆,直到隊列爲空爲止。
2、實現:
template<class T>
void BinaryTree<T>::levelOrder(void(*visit)(BinTreeNode<T>* p))
{
       queue<BinTreeNode<T>*> q;
       BinTreeNode<T>* t = root;
       if (t != nullptr) q.push(t);
       while (!q.empty())
       {
              t = q.front();
              q.pop();
              visit(t);
              if (t->leftChild != nullptr) q.push(t->leftChild);
              if (t->rightChild != nullptr) q.push(t->rightChild);
       }
}


<PS>:
以上算法實現代碼中使用的是STL中的stack,便於直接使用。

以上材料的大部分內容來自本人的數據結構老師-黃煜廉老師。













發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章