【數據結構】二叉樹遍歷

二叉樹的定義

    以遞歸形式給出的:一棵二叉樹是結點的一個有限集合,該集合或者爲空,或者是由一個根結點加上兩棵分別稱爲左子樹和右子樹的、互不相交的二叉樹組成。二又樹的特點是每個結點最多有兩個子女,分別稱爲該結點的左子女和右子女。在二又樹中不存在度大於2的結點,並且二又樹的子樹有左、右之分,其子樹的次序不能顛倒。二又樹是分支數最大不超過2的有根有序樹。它可能有5種不同的形態。

二叉樹的性質

 

二叉樹的數組存儲方式

 

    遇到空子樹,應在編號時假定有此子樹進行編號,而在順序存儲時當作有此子樹那樣把位置留出來。這樣才能反映二叉樹結點之間的相互關係,由其存儲位置找到它的父結點、子女、兄弟結點的位置。但這樣做有可能會消耗大量的存儲空間。例如:單支二叉樹,會浪費很多空間。

 

如果根節點編號是從1開始有有以下結論:

    中間節點一定在倒數第二層,最後一個節點的數就是總節點的個數,總結點數除2就是中間節點的數的個數,父節點的節點數*2<總節點個數,當前節點一定有兩個孩子,如果=就只有一個孩子,如果<就沒有一個孩子。

二叉樹的鏈表存儲表示

 

二叉樹結點類型的定義

複製代碼

複製代碼

1 template<typename T>
2 struct BinTreeNode
3 {
4     T data;    //結點中存儲的數據
5     BinTreeNode<T> *leftChild, *rightChild;    //左子樹和右子樹
6     BinTreeNode() :leftChild(NULL), rightChild(NULL) {}    //無參構造函數
7     BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) :data(x), leftChild(l), rightChild(r) {}    //帶默認值的有參構造參數
8 };

複製代碼

二叉樹的基本框架

 

複製代碼

複製代碼

//二叉樹

//結點類型
template <typename T>
struct BinTreeNode
{
    T data;                                                                                                        //結點中存儲的數據
    BinTreeNode<T> *leftChild, *rightChild;                                                                        //左子樹和右子樹
    BinTreeNode() : leftChild(NULL), rightChild(NULL) {}                                                           //無參構造函數
    BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) : data(x), leftChild(l), rightChild(r) {} //帶默認值的有參構造參數
};

//二叉樹類
template <typename T>
class BinaryTree
{
public:

//==========二叉樹構造與析構==========//

    //構造函數
    BinaryTree() : root(NULL) {}

    //指定結束標誌的構造函數
    BinaryTree(T value) : RefValue(value), root(NULL) {}

    //析構函數
    ~BinaryTree() { Destroy(root); }

//==========二叉樹的創建==========//

    //使用廣義表創建二叉樹,以'#'字符代表結束
    void CreateBinTree() { CreateBinTree(root); }

    //前序遍歷創建二叉樹(前序遍歷的應用),用#表示空結點
    void CreateBinTree_PreOrder() { CreateBinTree_PreOrder(root); }

    //使用先序遍歷和中序遍歷創建二叉樹
    void CreateBinTreeBy_Pre_In(const char *pre, const char *in)
    {
        int n = strlen(pre);
        CreateBinTreeBy_Pre_In(root, pre, in, n);
    }

    //使用後序遍歷和中序遍歷創建二叉樹
    void CreateBinTreeBy_Post_In(const char *post, const char *in)
    {
        int n = strlen(post);
        CreateBinTreeBy_Post_In(root, post, in, n);
    }

//==========二叉樹的遍歷==========//

    //先序遍歷(遞歸)
    void PreOrder() { PreOrder(root); }

    //中序遍歷(遞歸)
    void InOrder() { InOrder(root); }

    //後序遍歷(遞歸)
    void PostOrder() { PostOrder(root); }

    //先序遍歷(非遞歸)
    void PreOrder_NoRecurve() { PreOrder_NoRecurve(root); }

    //中序遍歷(非遞歸)
    void InOrder_NoRecurve() { InOrder_NoRecurve(root); }

    //後序遍歷(非遞歸)
    void PostOrder_NoRecurve() { PostOrder_NoRecurve(root); }

    //層次遍歷(非遞歸)
    void LevelOrder() { LevelOrder(root); }

//==========獲取結點==========//

    //獲取二叉樹的根節點
    BinTreeNode<T> *getRoot() const
    {
        return root;
    }

    //獲取current結點的父節點
    BinTreeNode<T> *Parent(BinTreeNode<T> *current)
    {
        return (root == NULL || root == current) ? NULL : Parent(root, current); //如果沒有根節點或current結點就是root結點,就沒有父節點
    }

    //獲取current結點的左結點
    BinTreeNode<T> *LeftChild(BinTreeNode<T> *current)
    {
        return (current != NULL) ? current->leftChild : NULL;
    }

    //獲取current結點的右結點
    BinTreeNode<T> *RightChild(BinTreeNode<T> *current)
    {
        return (current != NULL) ? current->rightChild : NULL;
    }
    
//==========成員函數==========//

    //銷燬函數
    void Destroy() { Destroy(root); }

    //拷貝二叉樹(前序遍歷的應用)
    BinaryTree(BinaryTree<T> &s)
    {
        root = Copy(s.getRoot());
    }

    //判斷兩顆二叉樹是否相等(前序遍歷的應用)
    bool operator==(BinaryTree<T> &s)
    {
        return (equal(this->getRoot(), s.getRoot()));
    }

    //計算二叉樹的結點的個數(後序遍歷的應用)
    int Size() { return Size(root); }

    //計算二叉樹的高度(後序遍歷的應用)
    int Height() { return Height(root); }

    //判斷二叉樹是否爲空
    bool Empty() { return (root == NULL) ? true : false; }

    //以廣義表的形式輸出二叉樹(前序遍歷的應用)
    void PrintBinTree() { PrintBinTree(root); }

private:
    BinTreeNode<T> *root; //根節點
    T RefValue;           //數據輸入停止的標誌,需要一個構造函數
};

複製代碼

 

二叉樹的創建

  1.使用廣義表創建

從廣義表A(B(D,E(G,)),C(,F))# 建立起來的二叉樹。

 

算法基本思路:

  1.若是字母(假定以字母作爲結點的值),則表示是結點的值,爲它建立一個新的結點,並把該結點作爲左子女(當k=1)或右子女(當k=2)鏈接到其父結點上。

  2.若是左括號"(",則表明子表的開始,將k置爲1;若遇到的是右括號")",則表明子表結束。

  3.若遇到的是逗號",",則表示以左子女爲根的子樹處理完畢,應接着處理以右子女爲根的子樹,將k置爲2。如此處理每一個字符,直到讀入結束符“#”爲止。

在算法中使用了一個棧s,在進入子表之前將根結點指針進棧,以便括號內的子女鏈接之用。在子表處理結束時退棧。

 

複製代碼

複製代碼

 1     //使用廣義表創建二叉樹函數,這裏以“字符”創建二叉樹,以'#'字符代表結束
 2     void CreateBinTree(BinTreeNode<T>* &BT)
 3     {
 4         stack< BinTreeNode<T>* > s;
 5         BT = NULL;
 6         BinTreeNode<T> *p, *t;    //p用來記住當前創建的節點,t用來記住棧頂的元素
 7         int k;    //k是處理左、右子樹的標記
 8         T ch;
 9         cin >> ch;
10 
11         while (ch != RefValue)
12         {
13             switch (ch)
14             {
15             case '(':    //對(做處理
16                 s.push(p);
17                 k = 1;
18                 break;
19 
20             case ')':    //對)做處理
21                 s.pop();
22                 break;
23 
24             case ',':    //對,做處理
25                 k = 2;
26                 break;
27 
28             default:
29                 p = new BinTreeNode<T>(ch);    //構造一個結點
30                 if (BT == NULL)    //如果頭節點是空
31                 {
32                     BT = p;
33                 }
34                 else if (k == 1)    //鏈入*t的左孩子
35                 {
36                     t = s.top();
37                     t->leftChild = p;
38                 }
39                 else    //鏈入*t的右孩子
40                 {
41                     t = s.top();
42                     t->rightChild = p;
43                 }
44             }
45             cin >> ch;
46         }
47     }

複製代碼

   2.使用已知的二叉樹的前序遍歷創建

  必須對應二又樹結點前序遍歷的順序,並約定以輸入序列中不可能出現的值作爲空結點的值以結束遞歸,此值通過構造函數存放在RefValue中。例如用“#”或表示字符序列或正整數序列空結點。

 

  前序遍歷所得到的前序序列爲ABC##DE#G##F###。

算法的基本思想是:

  每讀入一個值,就爲它建立結點。該結點作爲根結點,其地址通過函數的引用型參數subTree直接鏈接到作爲實際參數的指針中。然後,分別對根的左、右子樹遞歸地建立子樹,直到讀入“#”建立空子樹遞歸結束。

 

複製代碼

複製代碼

 1     //創建二叉樹(利用已知的二叉樹的前序遍歷創建)用#表示空結點
 2     void CreateBinTree_PreOrder(BinTreeNode<T>* &subTree)
 3     {
 4         T item;
 5         if (cin >> item)
 6         {
 7             if (item != RefValue)
 8             {
 9                 subTree = new BinTreeNode<T>(item);    //構造結點
10                 if (subTree == NULL)
11                 {
12                     cout << "空間分配錯誤!" << endl;
13                     exit(1);
14                 }
15                 CreateBinTree_PreOrder(subTree->leftChild);    //遞歸創建左子樹
16                 CreateBinTree_PreOrder(subTree->rightChild);    //遞歸創建右子樹
17             }
18             else
19             {
20                 subTree == NULL;
21             }
22         }
23     }

複製代碼

    3.根據已知的前序遍歷和中序遍歷創建二叉樹

        根據前序遍歷,先找到這棵樹的根節點,也就是數組受中第一個結點的位置,創建根節點。

        然後在中序遍歷中找到根的值所在的下標,切出左右子樹的前序和中序

        注意:如果前序遍歷的數組長度爲0,說明是一棵空樹。

舉例:

首先可以確定A是這棵樹的根節點,然後根據中序遍歷切出A的左右子樹,得到BCD屬於A的左子樹,E屬於A的右子樹,如下圖:

接着以B爲根節點,在中序遍歷中再次切出B的左右子樹,得到CD爲B的左子樹,右子樹爲空。

再以C爲根節點,結合中序遍歷,得到D爲C的右子樹,左子樹爲空。

創建好的二叉樹如下圖所示:

 

複製代碼

複製代碼

 1     //使用先序遍歷和中序遍歷創建二叉樹
 2     void CreateBinTreeBy_Pre_In(BinTreeNode<T> *&cur, const char *pre, const char *in, int n)
 3     {
 4         if (n <= 0)
 5         {
 6             cur = NULL;
 7             return;
 8         }
 9         int k = 0;
10         while (pre[0] != in[k]) //再中序中找到pre[0]的值
11         {
12             k++;
13         }
14         cur = new BinTreeNode<T>(in[k]); //創建結點
15         CreateBinTreeBy_Pre_In(cur->leftChild, pre + 1, in, k);
16         CreateBinTreeBy_Pre_In(cur->rightChild, pre + k + 1, in + k + 1, n - k - 1);
17     }

複製代碼

 

    4.根據已知的後續遍歷和中序遍歷創建二叉樹

        根據後序遍歷,先找到這棵樹的根節點的值,也就是數組中最後一個節點(數組長度-1)的位置,由此創建根節點。
        然後在中序遍歷中找到根的值所在的下標,切出左右子樹的後續和中序。
        注意:如果後序遍歷的數組長度爲0,說明是一棵空樹。

舉例:

由後序遍歷可以確定A是這棵樹的根節點,然後根據中序遍歷切出A的左右子樹,得到CDB屬於A的左子樹,E屬於A的右子樹,如下圖:

接着以B爲根節點,在中序遍歷中再次切出B的左右子樹,得到CD爲B的左子樹,右子樹爲空。

再以C爲根節點,結合中序遍歷,得到D爲C的右子樹,左子樹爲空。

創建好的二叉樹如下圖所示:

複製代碼

複製代碼

 1 //使用後序遍歷和中序遍歷創建二叉樹
 2     void CreateBinTreeBy_Post_In(BinTreeNode<T> *&cur, const char *post, const char *in, int n)
 3     {
 4         if (n == 0)
 5         {
 6             cur = NULL;
 7             return;
 8         }
 9 
10         char r = *(post + n - 1);    //根結點值
11         cur = new BinTreeNode<T>(r); //構造當前結點
12 
13         int k = 0;
14         const char *p = in;
15         while (*p != r)
16         {
17             k++;
18             p++;
19         }
20         CreateBinTreeBy_Post_In(cur->leftChild, post, in, k);
21         CreateBinTreeBy_Post_In(cur->rightChild, post + k, p + 1, n - k - 1);
22     }

複製代碼

 

二叉樹的遞歸遍歷

  先序遍歷:根->左->右

複製代碼

複製代碼

 1     //二叉樹的先序遍歷
 2     void PreOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             cout << subTree->data << " ";
 7             PreOrder(subTree->leftChild);
 8             PreOrder(subTree->rightChild);
 9         }
10     }

複製代碼

  中序遍歷:左->根->右

複製代碼

複製代碼

 1     //二叉樹的中序遍歷
 2     void InOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             InOrder(subTree->leftChild);
 7             cout << subTree->data << " ";
 8             InOrder(subTree->rightChild);
 9         }
10     }

複製代碼

  後續遍歷:左->右->根

複製代碼

複製代碼

 1     //二叉樹的後序遍歷
 2     void PostOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             PostOrder(subTree->leftChild);
 7             PostOrder(subTree->rightChild);
 8             cout << subTree->data << " ";
 9         }
10     }

複製代碼

 二叉樹的非遞歸遍歷

  先序遍歷

  爲了把一個遞歸過程改爲非遞歸過程,一般需要利用一個工作棧,記錄遍歷時的回退路徑。

 

  利用棧實現前序遍歷的過程。每次訪問一個結點後,在向左子樹遍歷下去之前,利用這個棧記錄該結點的右子女(如果有的話)結點的地址,以便在左子樹退回時可以直接從棧頂取得右子樹的根結點,繼續其右子樹的前序遍歷。

 

複製代碼

複製代碼

 1     //二叉樹先序遍歷(非遞歸1)
 2     void PreOrder_NoRecurve1(BinTreeNode<T> *p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         S.push(NULL);    //最先push一個NULL,到最後一個結點沒有左右子樹時,棧裏只有一個NULL了,令指針p指向這個NULL,再判斷就會結束循環
 6         while (p!=NULL)
 7         {
 8             cout << p->data << " ";
 9             if(p->rightChild!=NULL)    //預留右子樹指針在棧中
10             {
11                 S.push(p->rightChild);
12             }
13 
14             if (p->leftChild!=NULL)    //進左子樹
15             {
16                 p = p->leftChild;
17             }
18             else    //左子樹爲空
19             {
20                 p = S.top();
21                 S.pop();
22             }
23         }
24     }

複製代碼

     另一種前序遍歷的方法。爲了保證先左子樹後右子樹的順序,在進棧時是先進右子女結點地址,後進左子女結點地址,出棧時正好相反。

複製代碼

複製代碼

 1     //二叉樹先序遍歷(非遞歸2)
 2     void PreOrder_NoRecurve2(BinTreeNode<T> *p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         BinTreeNode<T>* t;
 6         S.push(p);    //根節點進棧
 7         while (!S.empty())    //當棧不爲空
 8         {
 9             t = S.top();    //p先記住棧頂元素,然後棧頂出棧
10             S.pop();
11             cout << t->data << " ";    //訪問元素
12             if (t->rightChild != NULL)    //右孩子不爲空,右孩子近棧
13             {
14                 S.push(t->rightChild);
15             }
16             if (t->leftChild != NULL)    //左孩子不爲空,左孩子進棧
17             {
18                 S.push(t->leftChild);
19             }
20         }
21     }

複製代碼

   中序遍歷

  需要使用一個棧,以記錄遍歷過程中回退的路徑。在一棵子樹中首先訪問的是中序下的第一個結點,它位於從根開始沿leftChild鏈走到最左下角的結點,該結點的leftChild指針爲NULL。訪問它的數據之後,再遍歷該結點的右子樹。此右子樹又是二叉樹,重複執行上面的過程,直到該子樹遍歷完。

 

  如果某結點的右子樹遍歷完或右子樹爲空,說明以這個結點爲根的二叉樹遍歷完,此時從棧中退出更上層的結點並訪問它,再向它的右子樹遍歷下去。

複製代碼

複製代碼

 1     //二叉樹的中序遍歷(非遞歸)
 2     void InOrder_NoRecurve(BinTreeNode<T>* p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         do
 6         {
 7             while (p!=NULL)
 8             {
 9                 S.push(p);
10                 p = p->leftChild;
11             }
12             if (!S.empty())
13             {
14                 p = S.top();
15                 S.pop();
16                 cout << p->data << " ";
17                 p = p->rightChild;
18             }
19         }
20         while (p!=NULL||!S.empty());
21     }

複製代碼

  後續遍歷

思想:

1、如果棧頂元素非空且左節點存在,將其壓入棧中,如果棧頂元素存在左節點,將其左節點壓棧,重複該過程。直到左結點不存在則進入第2步
2、判斷上一次出棧節點是否是當前棧頂結點的右節點(就是右葉子結點,如:g,f結點),或者當前棧頂結點不存在右結點(如:g,f,a結點),將當前節點輸出,並出棧。否則將當前棧頂結點右孩子節點壓棧,再進入第1步

複製代碼

複製代碼

 1     //後序遍歷(非遞歸)
 2     void PostOrder_NoRecurve(BinTreeNode<T> *p)
 3     {
 4         if (root == NULL)
 5             return;
 6         stack<BinTreeNode<T> *> s;
 7         s.push(p);
 8         BinTreeNode<T> *lastPop = NULL;
 9         while (!s.empty())
10         {
11             while (s.top()->leftChild != NULL)
12                 s.push(s.top()->leftChild);
13             while (!s.empty())
14             {
15                 //右葉子結點 || 沒有右結點
16                 if (lastPop == s.top()->rightChild || s.top()->rightChild == NULL)
17                 {
18                     cout << s.top()->data << " ";
19                     lastPop = s.top();
20                     s.pop();
21                 }
22                 else if (s.top()->rightChild != NULL)
23                 {
24                     s.push(s.top()->rightChild);
25                     break;
26                 }
27             }
28         }
29     }

複製代碼

 

  層次遍歷

    按層次順序訪問二叉樹的處理需要利用一個隊列。在訪問二又樹的某一層結點時,把下一層結點指針預先記憶在隊列中,利用隊列安排逐層訪問的次序。因此,每當訪問一個結點時,將它的子女依次加到隊列的隊尾,然後再訪問已在隊列隊頭的結點。這樣可以實現二又樹結點的按層訪問。

 

複製代碼

複製代碼

 1     //二叉樹的層次遍歷(非遞歸遍歷)
 2     void LevelOrder(BinTreeNode<T> *p)
 3     {
 4         queue<BinTreeNode<T>*> Q;
 5         Q.push(p);    //根節點進隊
 6         BinTreeNode<T>* t;
 7         while (!Q.empty())
 8         {
 9             t = Q.front();    //t先記住隊頭,再將隊頭出隊
10             Q.pop();
11             cout << t->data << " ";    //訪問隊頭元素的數據
12 
13             if (t->leftChild != NULL)
14             {
15                 Q.push(t->leftChild);
16             }
17 
18             if (t->rightChild != NULL)
19             {
20                 Q.push(t->rightChild);
21             }
22         }
23     }

複製代碼

 二叉樹的結點個數

複製代碼

複製代碼

1     //計算二叉樹以subTree爲根的結點的個數
2     int Size(BinTreeNode<T> *subTree) const
3     {
4         if (subTree == NULL)    //遞歸結束,空樹結點個數爲0
5         {
6             return 0;
7         }
8         return 1 + Size(subTree->leftChild) + Size(subTree->rightChild);
9     }

複製代碼

二叉樹的高度

複製代碼

複製代碼

 1     //計算二叉數以subTree爲根的高度
 2     int Height(BinTreeNode<T> *subTree)
 3     {
 4         if (subTree == NULL)    //遞歸結束,空樹高度爲0
 5         {
 6             return 0;
 7         }
 8         int i = Height(subTree->leftChild);
 9         int j = Height(subTree->rightChild);
10         return i < j ? j + 1 : i + 1;
11     }

複製代碼

 以廣義表的形式輸出二叉樹

複製代碼

複製代碼

 1     void PrintBinTree(BinTreeNode<T> *BT)
 2     {
 3         if (BT != NULL)    //樹爲空時結束遞歸
 4         {
 5             cout << BT->data;
 6             if (BT->leftChild != NULL || BT->rightChild != NULL)
 7             {
 8                 cout << '(';
 9                 if (BT->leftChild!=NULL)
10                 {
11                     PrintBinTree(BT->leftChild);
12                 }
13                 cout << ',';
14                 if (BT->rightChild != NULL)
15                 {
16                     PrintBinTree(BT->rightChild);
17                 }
18                 cout << ')';
19             }
20         }
21     }

複製代碼

求二叉樹某結點的父節點

 

 1     //從結點subTree開始,搜索結點current的父節點,找到返回父節點的地址,找不到返回NULL
 2     BinTreeNode<T>* Parent(BinTreeNode<T>* subTree, BinTreeNode<T>* current)
 3     {
 4         if (subTree == NULL)
 5         {
 6             return NULL;
 7         }
 8         if (subTree->leftChild == current || subTree->rightChild == current)    //如果找到,返回父節點subTree
 9         {
10             return subTree;
11         }
12         BinTreeNode<T>* p;
13         if (p = Parent(subTree->leftChild, current) != NULL)    //遞歸在左子樹中搜索
14         {
15             return p;
16         }
17         else
18         {
19             return Parent(subTree->rightChild, current);    //遞歸右子樹中搜索
20         }
21     }

 

二叉樹的銷燬

複製代碼

複製代碼

 1     //二叉樹的銷燬函數
 2     void Destroy(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             Destroy(subTree->leftChild);
 7             Destroy(subTree->rightChild);
 8             delete subTree;
 9             subTree = NULL;
10         }
11     }

複製代碼

 判斷兩顆二叉樹是否相等

複製代碼

複製代碼

 1     //判斷兩顆二叉樹是否相等
 2     bool equal(BinTreeNode<T> *a, BinTreeNode<T> *b)
 3     {
 4         if (a == NULL&&b == NULL)    //兩者都爲空
 5         {
 6             return true;
 7         }
 8         if (a != NULL&&b != NULL&&a->data == b->data&&equal(a->leftChild, b->leftChild) && equal(a->rightChild, b->rightChild))    //兩者都不爲空,且兩者的結點數據相等,且兩者的左右子樹的結點都相等
 9         {
10             return true;
11         }
12         return false;
13     }

複製代碼

 

 三種遍歷方式的作用:

先序遍歷:在第一次遍歷到節點時就執行操作,一般只是想遍歷執行操作(或輸出結果)可選用先序遍歷;已知後序遍歷和中序遍歷,就能確定前序遍歷。

中序遍歷:對於二分搜索樹,中序遍歷的操作順序(或輸出結果順序)是符合從小到大(或從大到小)順序的,故要遍歷輸出排序好的結果需要使用中序遍歷

後序遍歷:後續遍歷的特點是執行操作時,肯定已經遍歷過該節點的左右子節點,故適用於要進行破壞性操作的情況,比如刪除所有節點;已知前序遍歷和中序遍歷,就能確定後序遍歷。

 

 完整代碼:

 

複製代碼

複製代碼

  1 //結點類型
  2 template <typename T>
  3 struct BinTreeNode
  4 {
  5     T data;                                                                                                        //結點中存儲的數據
  6     BinTreeNode<T> *leftChild, *rightChild;                                                                        //左子樹和右子樹
  7     BinTreeNode() : leftChild(NULL), rightChild(NULL) {}                                                           //無參構造函數
  8     BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) : data(x), leftChild(l), rightChild(r) {} //帶默認值的有參構造參數
  9 };
 10 
 11 //二叉樹類
 12 template <typename T>
 13 class BinaryTree
 14 {
 15 public:
 16 
 17 //==========二叉樹構造與析構==========//
 18 
 19     //構造函數
 20     BinaryTree() : root(NULL) {}
 21 
 22     //指定結束標誌的構造函數
 23     BinaryTree(T value) : RefValue(value), root(NULL) {}
 24 
 25     //析構函數
 26     ~BinaryTree() { Destroy(root); }
 27 
 28 //==========二叉樹的創建==========//
 29 
 30     //使用廣義表創建二叉樹,以'#'字符代表結束
 31     void CreateBinTree() { CreateBinTree(root); }
 32 
 33     //前序遍歷創建二叉樹(前序遍歷的應用),用#表示空結點
 34     void CreateBinTree_PreOrder() { CreateBinTree_PreOrder(root); }
 35 
 36     //使用先序遍歷和中序遍歷創建二叉樹
 37     void CreateBinTreeBy_Pre_In(const char *pre, const char *in)
 38     {
 39         int n = strlen(pre);
 40         CreateBinTreeBy_Pre_In(root, pre, in, n);
 41     }
 42 
 43     //使用後序遍歷和中序遍歷創建二叉樹
 44     void CreateBinTreeBy_Post_In(const char *post, const char *in)
 45     {
 46         int n = strlen(post);
 47         CreateBinTreeBy_Post_In(root, post, in, n);
 48     }
 49 
 50 //==========二叉樹的遍歷==========//
 51 
 52     //先序遍歷(遞歸)
 53     void PreOrder() { PreOrder(root); }
 54 
 55     //中序遍歷(遞歸)
 56     void InOrder() { InOrder(root); }
 57 
 58     //後序遍歷(遞歸)
 59     void PostOrder() { PostOrder(root); }
 60 
 61     //先序遍歷(非遞歸)
 62     void PreOrder_NoRecurve() { PreOrder_NoRecurve(root); }
 63 
 64     //中序遍歷(非遞歸)
 65     void InOrder_NoRecurve() { InOrder_NoRecurve(root); }
 66 
 67     //後序遍歷(非遞歸)
 68     void PostOrder_NoRecurve() { PostOrder_NoRecurve(root); }
 69 
 70     //層次遍歷(非遞歸)
 71     void LevelOrder() { LevelOrder(root); }
 72 
 73 //==========獲取結點==========//
 74 
 75     //獲取二叉樹的根節點
 76     BinTreeNode<T> *getRoot() const
 77     {
 78         return root;
 79     }
 80 
 81     //獲取current結點的父節點
 82     BinTreeNode<T> *Parent(BinTreeNode<T> *current)
 83     {
 84         return (root == NULL || root == current) ? NULL : Parent(root, current); //如果沒有根節點或current結點就是root結點,就沒有父節點
 85     }
 86 
 87     //獲取current結點的左結點
 88     BinTreeNode<T> *LeftChild(BinTreeNode<T> *current)
 89     {
 90         return (current != NULL) ? current->leftChild : NULL;
 91     }
 92 
 93     //獲取current結點的右結點
 94     BinTreeNode<T> *RightChild(BinTreeNode<T> *current)
 95     {
 96         return (current != NULL) ? current->rightChild : NULL;
 97     }
 98 
 99 //==========成員函數==========//
100 
101     //銷燬函數
102     void Destroy() { Destroy(root); }
103 
104     //拷貝二叉樹(前序遍歷的應用)
105     BinaryTree(BinaryTree<T> &s)
106     {
107         root = Copy(s.getRoot());
108     }
109 
110     //判斷兩顆二叉樹是否相等(前序遍歷的應用)
111     bool operator==(BinaryTree<T> &s)
112     {
113         return (equal(this->getRoot(), s.getRoot()));
114     }
115 
116     //計算二叉樹的結點的個數(後序遍歷的應用)
117     int Size() { return Size(root); }
118 
119     //計算二叉樹的高度(後序遍歷的應用)
120     int Height() { return Height(root); }
121 
122     //判斷二叉樹是否爲空
123     bool Empty() { return (root == NULL) ? true : false; }
124 
125     //以廣義表的形式輸出二叉樹(前序遍歷的應用)
126     void PrintBinTree() { PrintBinTree(root); }
127 
128 protected:
129 
130     //使用廣義表創建二叉樹函數,這裏以“字符”創建二叉樹,以'#'字符代表結束
131     void CreateBinTree(BinTreeNode<T> *&BT)
132     {
133         stack<BinTreeNode<T> *> s;
134         BT = NULL;
135         BinTreeNode<T> *p, *t; //p用來記住當前創建的節點,t用來記住棧頂的元素
136         int k;                 //k是處理左、右子樹的標記
137         T ch;
138         cin >> ch;
139 
140         while (ch != RefValue)
141         {
142             switch (ch)
143             {
144             case '(': //對(做處理
145                 s.push(p);
146                 k = 1;
147                 break;
148 
149             case ')': //對)做處理
150                 s.pop();
151                 break;
152 
153             case ',': //對,做處理
154                 k = 2;
155                 break;
156 
157             default:
158                 p = new BinTreeNode<T>(ch); //構造一個結點
159                 if (BT == NULL)             //如果頭節點是空
160                 {
161                     BT = p;
162                 }
163                 else if (k == 1) //鏈入*t的左孩子
164                 {
165                     t = s.top();
166                     t->leftChild = p;
167                 }
168                 else //鏈入*t的右孩子
169                 {
170                     t = s.top();
171                     t->rightChild = p;
172                 }
173             }
174             cin >> ch;
175         }
176     }
177 
178     //創建二叉樹(利用已知的二叉樹的前序遍歷創建)用#表示空結點
179     void CreateBinTree_PreOrder(BinTreeNode<T> *&subTree)
180     {
181         T item;
182         if (cin >> item)
183         {
184             if (item != RefValue)
185             {
186                 subTree = new BinTreeNode<T>(item); //構造結點
187                 if (subTree == NULL)
188                 {
189                     cout << "空間分配錯誤!" << endl;
190                     exit(1);
191                 }
192                 CreateBinTree_PreOrder(subTree->leftChild);  //遞歸創建左子樹
193                 CreateBinTree_PreOrder(subTree->rightChild); //遞歸創建右子樹
194             }
195             else
196             {
197                 subTree == NULL;
198             }
199         }
200     }
201 
202     //使用先序遍歷和中序遍歷創建二叉樹
203     void CreateBinTreeBy_Pre_In(BinTreeNode<T> *&cur, const char *pre, const char *in, int n)
204     {
205         if (n <= 0)
206         {
207             cur = NULL;
208             return;
209         }
210         int k = 0;
211         while (pre[0] != in[k]) //再中序中找到pre[0]的值
212         {
213             k++;
214         }
215         cur = new BinTreeNode<T>(in[k]); //創建結點
216         CreateBinTreeBy_Pre_In(cur->leftChild, pre + 1, in, k);
217         CreateBinTreeBy_Pre_In(cur->rightChild, pre + k + 1, in + k + 1, n - k - 1);
218     }
219     //使用後序遍歷和中序遍歷創建二叉樹
220     void CreateBinTreeBy_Post_In(BinTreeNode<T> *&cur, const char *post, const char *in, int n)
221     {
222         if (n == 0)
223         {
224             cur = NULL;
225             return;
226         }
227 
228         char r = *(post + n - 1);    //根結點值
229         cur = new BinTreeNode<T>(r); //構造當前結點
230 
231         int k = 0;
232         const char *p = in;
233         while (*p != r)
234         {
235             k++;
236             p++;
237         }
238         CreateBinTreeBy_Post_In(cur->leftChild, post, in, k);
239         CreateBinTreeBy_Post_In(cur->rightChild, post + k, p + 1, n - k - 1);
240     }
241 
242     //先序遍歷(遞歸)
243     void PreOrder(BinTreeNode<T> *&subTree)
244     {
245         if (subTree != NULL)
246         {
247             cout << subTree->data << " ";
248             PreOrder(subTree->leftChild);
249             PreOrder(subTree->rightChild);
250         }
251     }
252 
253     //中序遍歷(遞歸)
254     void InOrder(BinTreeNode<T> *&subTree)
255     {
256         if (subTree != NULL)
257         {
258             InOrder(subTree->leftChild);
259             cout << subTree->data << " ";
260             InOrder(subTree->rightChild);
261         }
262     }
263 
264     //後序遍歷(遞歸)
265     void PostOrder(BinTreeNode<T> *&subTree)
266     {
267         if (subTree != NULL)
268         {
269             PostOrder(subTree->leftChild);
270             PostOrder(subTree->rightChild);
271             cout << subTree->data << " ";
272         }
273     }
274 
275     //先序遍歷(非遞歸)
276     void PreOrder_NoRecurve(BinTreeNode<T> *p)
277     {
278         stack<BinTreeNode<T> *> S;
279         BinTreeNode<T> *t;
280         S.push(p);         //根節點進棧
281         while (!S.empty()) //當棧不爲空
282         {
283             t = S.top(); //p先記住棧頂元素,然後棧頂出棧
284             S.pop();
285             cout << t->data << " ";    //訪問元素
286             if (t->rightChild != NULL) //右孩子不爲空,右孩子近棧
287             {
288                 S.push(t->rightChild);
289             }
290             if (t->leftChild != NULL) //左孩子不爲空,左孩子進棧
291             {
292                 S.push(t->leftChild);
293             }
294         }
295     }
296 
297     //中序遍歷(非遞歸)
298     void InOrder_NoRecurve(BinTreeNode<T> *root)
299     {
300         if (root == NULL)
301             return;
302         stack<BinTreeNode<T> *> s;
303         s.push(root);
304         while (!s.empty())
305         {
306             while (s.top()->leftChild != NULL) //將左結點依次入棧
307             {
308                 s.push(s.top()->leftChild);
309             }
310             while (!s.empty())
311             {
312                 BinTreeNode<T> *cur = s.top();
313                 cout << cur->data << " ";
314                 s.pop();
315                 if (cur->rightChild != NULL)
316                 {
317                     s.push(cur->rightChild);
318                     break;
319                 }
320             }
321         }
322     }
323 
324     //後序遍歷(非遞歸)
325     void PostOrder_NoRecurve(BinTreeNode<T> *p)
326     {
327         if (root == NULL)
328             return;
329         stack<BinTreeNode<T> *> s;
330         s.push(p);
331         BinTreeNode<T> *lastPop = NULL;
332         while (!s.empty())
333         {
334             while (s.top()->leftChild != NULL)
335                 s.push(s.top()->leftChild);
336             while (!s.empty())
337             {
338                 //右葉子結點 || 沒有右結點
339                 if (lastPop == s.top()->rightChild || s.top()->rightChild == NULL)
340                 {
341                     cout << s.top()->data << " ";
342                     lastPop = s.top();
343                     s.pop();
344                 }
345                 else if (s.top()->rightChild != NULL)
346                 {
347                     s.push(s.top()->rightChild);
348                     break;
349                 }
350             }
351         }
352     }
353 
354     //層次遍歷(非遞歸)
355     void LevelOrder(BinTreeNode<T> *p)
356     {
357         queue<BinTreeNode<T> *> Q;
358         Q.push(p); //根節點進隊
359         BinTreeNode<T> *t;
360         while (!Q.empty())
361         {
362             t = Q.front(); //t先記住隊頭,再將隊頭出隊
363             Q.pop();
364             cout << t->data << " "; //訪問隊頭元素的數據
365 
366             if (t->leftChild != NULL)
367             {
368                 Q.push(t->leftChild);
369             }
370 
371             if (t->rightChild != NULL)
372             {
373                 Q.push(t->rightChild);
374             }
375         }
376     }
377 
378     //從結點subTree開始,搜索結點current的父節點,找到返回父節點的地址,找不到返回NULL
379     BinTreeNode<T> *Parent(BinTreeNode<T> *subTree, BinTreeNode<T> *current)
380     {
381         if (subTree == NULL)
382         {
383             return NULL;
384         }
385         if (subTree->leftChild == current || subTree->rightChild == current) //如果找到,返回父節點subTree
386         {
387             return subTree;
388         }
389         BinTreeNode<T> *p;
390         if (p = Parent(subTree->leftChild, current) != NULL) //遞歸在左子樹中搜索
391         {
392             return p;
393         }
394         else
395         {
396             return Parent(subTree->rightChild, current); //遞歸右子樹中搜索
397         }
398     }
399 
400     //二叉樹的銷燬
401     void Destroy(BinTreeNode<T> *&subTree)
402     {
403         if (subTree != NULL)
404         {
405             Destroy(subTree->leftChild);
406             Destroy(subTree->rightChild);
407             delete subTree;
408             subTree = NULL;
409         }
410     }
411 
412     //複製二叉樹函數,返回一個指針,給出一個以originNode爲根複製的二叉樹的副本
413     BinTreeNode<T> *Copy(BinTreeNode<T> *originNode)
414     {
415         if (originNode == NULL)
416         {
417             return NULL;
418         }
419         BinTreeNode<T> *temp = new BinTreeNode<T>; //創建根結點
420         temp->data = originNode->data;
421         temp->leftChild = Copy(originNode->leftChild);
422         temp->rightChild = Copy(originNode->rightChild);
423         return temp;
424     }
425 
426     //判斷兩顆二叉樹是否相等
427     bool equal(BinTreeNode<T> *a, BinTreeNode<T> *b)
428     {
429         if (a == NULL && b == NULL) //兩者都爲空
430         {
431             return true;
432         }
433         if (a != NULL && b != NULL && a->data == b->data && equal(a->leftChild, b->leftChild) && equal(a->rightChild, b->rightChild)) //兩者都不爲空,且兩者的結點數據相等,且兩者的左右子樹的結點都相等
434         {
435             return true;
436         }
437         return false;
438     }
439 
440     //計算二叉樹以subTree爲根的結點的個數
441     int Size(BinTreeNode<T> *subTree) const
442     {
443         if (subTree == NULL) //遞歸結束,空樹結點個數爲0
444         {
445             return 0;
446         }
447         return 1 + Size(subTree->leftChild) + Size(subTree->rightChild);
448     }
449 
450     //計算二叉數以subTree爲根的高度
451     int Height(BinTreeNode<T> *subTree)
452     {
453         if (subTree == NULL) //遞歸結束,空樹高度爲0
454         {
455             return 0;
456         }
457         int i = Height(subTree->leftChild);
458         int j = Height(subTree->rightChild);
459         return i < j ? j + 1 : i + 1;
460     }
461 
462     //以廣義表的形式輸出二叉樹
463     void PrintBinTree(BinTreeNode<T> *BT)
464     {
465         if (BT != NULL) //樹爲空時結束遞歸
466         {
467             cout << BT->data;
468             if (BT->leftChild != NULL || BT->rightChild != NULL)
469             {
470                 cout << '(';
471                 if (BT->leftChild != NULL)
472                 {
473                     PrintBinTree(BT->leftChild);
474                 }
475                 cout << ',';
476                 if (BT->rightChild != NULL)
477                 {
478                     PrintBinTree(BT->rightChild);
479                 }
480                 cout << ')';
481             }
482         }
483     }
484 
485 private:
486     BinTreeNode<T> *root; //根節點
487     T RefValue;           //數據輸入停止的標誌,需要一個構造函數
488 };
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章