遍歷二叉樹-------遞迴&非遞迴

慵懶de瘋子發表於2018-04-10

一、什麼是樹?什麼又是二叉樹
(1)樹的定義
樹是一種特殊的資料結構,是由N(N>=0)個節點構成的具有層次關係的集合。每棵樹都有一個特殊的結點,就是根結點,根節點沒有前驅結點;除過根節點的其餘結點被分為若干個互不相交的集合,稱之為子樹;每個子樹的根結點有且僅有一個前驅,可以有0個或者多個後繼
這裡寫圖片描述
(2)那麼問題來了,什麼是二叉樹嘞?

二叉樹是每個結點最多有兩個子樹的有序樹(它由一個根節點加上它的左右子樹組合起來的)。

* 特點:(1)每個結點最多有兩棵子樹,即二叉樹不存在度大於2的結點(分支數最大不超過2)

       (2)二叉樹的子樹有左右之分,其子樹的次序不能被顛倒


* 二叉樹有五種基本形態:(如下圖所示)

            (1)空二叉樹
            (2)只有一個根節點
            (3)只有左子樹
            (4)只有右子樹
            (5)完全二叉樹

這裡寫圖片描述

  • 滿二叉樹:一棵二叉樹上的所有分支結點都存在左右子樹,並且葉子結點都在同一層上面
    這裡寫圖片描述
  • 完全二叉樹:如果一個具有N個結點的二叉樹與之滿二叉樹的前N個結點的結構相同,就是完全二叉樹
    這裡寫圖片描述

二叉樹的四種遍歷

1前序遍歷(VLR):根節點—>左子樹—>右子樹
這裡寫圖片描述
【遞迴遍歷】
演算法如下:
判斷二叉樹是否為空,倘若為空,則return;否則:

* (1)先訪問根節點
* (2)然後前序遍歷根節點的左子樹
* (3)最後前序遍歷根節點的右子樹

程式碼實現:

      //前序遞迴遍歷
      void PreOrder()
      {
            cout<<"前序遍歷"<<endl;
            _PreOrder(_pRoot);
            cout<<endl;
      }

      //前序遞迴: 根---根的左子樹---根的右子樹
      void _PreOrder(PNode& pRoot)
      {
            if(pRoot)
            {
                  cout<< pRoot->_data << "";
                  _PreOrder(pRoot->_pLeft);
                  _PreOrder(pRoot->_pRight);
            }
      }

【非遞迴遍歷】:(藉助棧實現)
演算法如下:
判斷二叉樹是否為空,倘若為空,則return;否則:
(1)初始化一個棧
(2)根節點入棧
(3)當棧不為空時,迴圈執行下列步驟

* 訪問取出棧頂元素
* 倘若該被訪問的節點的左子樹非空時,將該結點左子樹指標入棧
* 倘若該被訪問的節點的右子樹非空時,將該結點右子樹指標入棧

(4)重複執行直至棧為空,return

程式碼實現:

      //前序非遞迴
      void PreOrder_N()
      {
            _PreOrder_N(_pRoot);
      }
      //前序非遞迴
      void _PreOrder_N(PNode& pRoot)
      {
            //判斷樹是否存在
            if(NULL == pRoot)
                  return;
            stack<PNode> s;
            s.push(pRoot);
            while(!s.empty())
            {
                  PNode pTop = s.top();
                  cout<<pTop->_data<<"";
                  if(pTop->_pLeft)
                        s.push(pTop->_pLeft);
                  if(pTop->_pRight)
                        s.push(pTop->_pRight);
            }
            cout<<endl;
      }

2中序遍歷(LVR):左子樹—>根節點—>右子樹
這裡寫圖片描述
【遞迴遍歷】
演算法如下:
判斷二叉樹是否為空,倘若為空,則return;否則:

* (1)先中序遍歷根節點的左子樹
* (2)訪問根節點
* (3)最後中序遍歷根節點的右子樹

程式碼實現:

      //中序遞迴遍歷
      void InOrder()
      {
            cout<<"中序遍歷"<<endl;
            _InOrder(_pRoot);
            cout<<" "<<endl;
      }

      //中序遞迴: 左子樹---根節點---右子樹
      void _InOrder(PNode pRoot)
      {
            if(pRoot)
            {
                  _InOrder(pRoot->_pLeft);
                  cout<< pRoot->_data <<"";
                  _InOrder(pRoot->_pRight);
            }
      }

【非遞迴遍歷】
演算法如下:

判斷二叉樹是否為空,倘若為空,則return;否則:
(1)初始化一個棧
(2)給定一個任意節點pCur,倘若pCur的左孩子不為空時,則將其入棧,並將pCur指向pCur的左孩子;(一直迴圈執行,直至左孩子為空)
(3)執行完步驟(2)後,說明最左邊的結點已被找到,訪問輸出棧頂元素,並進行出棧操作
(4)將pCur指向其右孩子
(5)重複2、3、4步驟,直至當前結點為空或者棧為空,return

程式碼實現:

      //中序非遞迴
      void InOrder_N()
      {
            _InOrder_N(_pRoot);
      }
      //中序非遞迴
      void _InOrder_N(PNode pRoot)
      {
            if(NULL == pRoot)
                  return;
            stack<PNode> s;
            PNode pCur = pRoot;
            while(pCur || !s.empty())
            {
                  while(pCur)
                  {
                        s.push(pCur);
                        pCur = pCur->_pLeft;
                  }
                  pCur = s.top();
                  cout<<pCur->_data<<"";
                  s.pop();
                  pCur = pCur->_pRight;
            }
      }

3、後序遍歷(LRV):左子樹—>右子樹—>根節點
這裡寫圖片描述
【遞迴遍歷】
演算法如下:
判斷二叉樹是否為空,倘若為空,則return;否則:

* (1)先後序遍歷根節點的左子樹
* (2)後序遍歷根節點的右子樹
* (3)訪問根節點

程式碼實現:

      //後序遞迴遍歷
      void PostOrder()
      {
            cout<<"後序遍歷"<<endl;
            _PostOrder(_pRoot);
            cout<<""<<endl;
      }

      //後序遞迴: 左子樹---右子樹---根節點
      void _PostOrder(PNode pRoot)
      {
            if(pRoot)
            {
                  _PostOrder(pRoot->_pLeft);
                  _PostOrder(pRoot->_pRight);
                  cout<< pRoot->_data <<"";
            }
      }

【非遞迴遍歷】:出棧順序為左右根,則壓棧順序為根右左
演算法如下:

判斷二叉樹是否為空,倘若為空,則return;否則:
(1)初始化一個棧
(2)給定一個任意節點pCur
(3)將PCur入棧,倘若pCur的左孩子不為空時,將pCur指向pCur的左孩子;(一直迴圈執行,直至左孩子為空)
(4)執行完步驟(2)後,說明最左邊的結點已被找到,訪問棧頂元素
(5)此時倘若棧頂元素沒有右孩子,或者有右孩子但已被訪問輸出;則直接列印輸出該結點,並將其出棧
(6)否則當不滿足(4)的情況時,將該結點的右孩子給pCur
(7)重複3、4、5、6步驟,直至棧為空,return

程式碼實現:

      //後序非遞迴
      void PostOrder_N()
      {
            _PostOrder_N(_pRoot);
      }
      //後序非遞迴
      void _PostOrder_N(PNode pRoot)
      {
            if(NULL == pRoot)
                  return;
            stack<PNode> s;
            PNode pCur = pRoot;
            PNode prev = NULL:
            while()
            {
                  while()
                  {
                        s.push(pCur);
                        pCur = pCur->_pLeft;
                  }
                  pTop = s.top();
                  if(NULL == pTop->_pRight || prev == pTop->_pRight)
                  {
                        cout<<pTop->_data<<"";
                        s.pop;
                  }
                  else
                  {
                        pCur = pTop->_pRight;
                  }
            }
      }

4、層序遍歷(非遞迴)

層序遍歷就是:從根節點到葉子結點按照同一層先左子樹後右子樹的次序遍歷二叉樹。層序遍歷用佇列的方式實現
這裡寫圖片描述
演算法如下:
(1)初始化一個佇列
(2)將根節點的指標入佇列,即插入隊尾
(3)倘若佇列非空,則迴圈執行下列步驟:

* 從對頭取出一個結點訪問
* 倘若被訪問的結點非空,則讓其左子樹指標入佇列
* 倘若被訪問的結點非空,則讓其左子樹指標入佇列

(4)佇列清空後,return
程式碼實現:

      //層序遍歷
      void LevelOrder()
      {
            cout<<"層序遍歷"<<endl;
            _LevelOrder(_pRoot);
            cout<<""<<endl;
      }

      //層序遍歷
      void _LevelOrder(PNode pRoot)
      {
            if(pRoot)
            {
                  queue<PNode> q;
                  q.push(pRoot);
                  while(!q.empty())
                  {
                        PNode pCur = q.front();
                        cout<< pCur->_data <<"";
                        q.pop();
                        if(pCur->_pLeft)
                              q.push(pCur->_pLeft);
                        if(pCur->_pRight)
                              q.push(pCur->_pRight);
                  }
            }
      }

相關文章