二叉樹的定義
以遞歸形式給出的:一棵二叉樹是結點的一個有限集合,該集合或者爲空,或者是由一個根結點加上兩棵分別稱爲左子樹和右子樹的、互不相交的二叉樹組成。二又樹的特點是每個結點最多有兩個子女,分別稱爲該結點的左子女和右子女。在二又樹中不存在度大於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 };