啓迪思惟:二叉樹

不少初級點的程序員會認爲樹結構無用論,也有初級程序員僅僅覺得只有面試纔會用到,還有自認爲實際工做用不到(我身邊工做好幾年程序員懂樹結構也沒有幾個),其實歸根到底仍是不清楚樹的實際用途,下面分享我參加培訓時候一個小尷尬。html

 

由於項目數據量很大(有不少表數據量都上億的),對寫sql能力要求很高,項目組會常常組織些數據庫方面的培訓,前段時間又參加公司一個SQL原理分析的一個培訓,在培訓中講師問「爲何SQL走索引查詢速度很快呢?」,我直接大聲說「索引底層數據結構是B樹,查詢的時候用二分查找」,結果整個大房間就我一我的聲音,全部同事都看過來,場面有點尷尬。node

 

上一篇文章分析樹基本概念、名詞解釋、樹三種遍歷方式,今天繼續來看二叉樹各類名詞機率,看這些名詞概念,確定是各類不爽,大概瀏覽下知道怎麼回事就ok程序員

 

一:概念,下面這些內容摘自維基百科面試

在計算機科學中,二叉樹是每一個節點最多有兩個子樹的樹結構。一般子樹被稱做「左子樹」(left subtree)和「右子樹」(right subtree)。二叉樹常被用於實現二叉查找樹和二叉堆。算法

二叉樹的每一個結點至多隻有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。二叉樹的第i層至多有個結點;深度爲k的二叉樹至多有個結點;對任何一棵二叉樹T,若是其終端結點數爲,度爲2的結點數爲,則。sql

樹和二叉樹的三個主要差異:數據庫

樹的結點個數至少爲1,而二叉樹的結點個數能夠爲0編程

樹中結點的最大度數沒有限制,而二叉樹結點的最大度數爲2數據結構

樹的結點無左、右之分,而二叉樹的結點有左、右之分。app

徹底二叉樹和滿二叉樹

滿二叉樹:一棵深度爲k,且有個節點成爲滿二叉樹

徹底二叉樹:深度爲k,有n個節點的二叉樹,當且僅當其每個節點都與深度爲k的滿二叉樹中序號爲1n的節點對應時,稱之爲徹底二叉樹

 

二:示例圖

 

三:動畫圖

四:代碼分析

 一、是否爲空

1 /**
2  * 若樹爲空,則返回true;不然返回false
3  */
4 bool IsEmpty() {
5     return root == 0;
6 }

二、計算樹的深度

 1 /**
 2  * 以傳入節點爲基礎計算樹的深度
 3  */
 4 int GetTreeDept(const TNode<T> *t) {
 5     int i, j;
 6     if (t == 0) {
 7         return 0;
 8     } else {
 9         //遞歸計算左子樹的深度
10         i = this->GetTreeDept(t->lchild);
11         //遞歸計算右子樹的深度
12         j = this->GetTreeDept(t->rchild);
13     }
14 
15     //t的深度爲其左右子樹中深度中的大者加1
16     return i > j ? i + 1 : j + 1;
17 }

三、清空樹的節點

 1 /**
 2  *清空樹的全部節點
 3  */
 4 void Clear() {
 5     Clear(root);
 6 }
 7 
 8 /**
 9  *根據節點遞歸清空樹
10  */
11 void Clear(TNode<T>* t) {
12     //判斷指針是否爲空
13     if (t) {
14         //獲取資源當即放入管理對象(參考Effective C++裏邊條款13)
15         //tr1::shared_ptr(引用計數智慧指針)管理對象比auto_ptr更強大
16         std::auto_ptr<TNode<T> > new_ptr(t);
17 
18         //遞歸清空右子樹
19         Clear(new_ptr->rchild);
20 
21         //遞歸清空左子樹
22         Clear(new_ptr->lchild);
23     }
24 
25     //清空樹的根節點
26     t = 0;
27 }

四、獲取樹最大節點和最小節點

 1 /**
 2  *獲取樹的最大節點
 3  */
 4 TNode<T>* GetMax(TNode<T>* t) const {
 5     //判斷數節點是否爲空
 6     if (t) {
 7         //根據二叉樹特性,最大值必定在右子樹;
 8         //循環右子樹,直到葉子節點
 9         while (t->rchild) {
10             //指向下一個節點
11             t = t->rchild;
12         }
13     }
14     //返回找到最大節點
15     return t;
16 }
17 /**
18  *獲取樹的最小節點
19  */
20 TNode<T>* GetMin(TNode<T>* t) const {
21     //判斷數節點是否爲空
22     if (t) {
23         //根據二叉樹特性,最大值必定在左子樹;
24         //循環左子樹,直到葉子節點
25         while (t->lchild) {
26             //指向下一個節點
27             t = t->lchild;
28         }
29     }
30     //返回找到最小節點
31     return t;
32 }
33 /**
34  *根據模式類型查找樹的最大值或者最小值
35  */
36 TNode<T>* GetNode(Mode mode) const {
37     //t指向根節點
38     TNode<T>* t = root;
39     //判斷數節點是否爲空
40     if (t) {
41         if (mode == Min) {
42             //根據二叉樹特性,最大值必定在左子樹;
43             //循環左子樹,直到葉子節點
44             while (t->lchild) {
45                 //指向左子樹下一個節點
46                 t = t->lchild;
47             }
48         } else if (mode == Max) {
49             //根據二叉樹特性,最大值必定在右子樹;
50             //循環右子樹,直到葉子節點
51             while (t->rchild) {
52                 //指向右子樹下一個節點
53                 t = t->rchild;
54             }
55         }
56     }
57     //返回找到節點
58     return t;
59 }

五、獲取傳入節點父節點

 1 /**
 2  *獲取傳入的節點從傳入樹p中找到它的父節點
 3  */
 4 TNode<T>* GetParentNode(TNode<T> *p, const T &value,TNode<T> *returnValue) {
 5     //p節點存在而且傳入的值不是根節點值
 6     if (p && p->data == value) {
 7         return 0;
 8     }
 9 
10     //用二分查找定位值value所在節點
11     TNode<T> *t = this->SearchTree(p, value);
12 
13     //判斷t和p都不爲空
14     if (t && p) {
15         //若是value的節點等於p節點左孩子或者右孩子,p就是value的節點父親
16         if (p->lchild == t || p->rchild == t) {
17             //賦值p節點給返回值變量
18             returnValue = p;
19         } else if (value > p->data) {//若是value只大於p節點值,則遞歸右孩子
20             //直到找到value的父節點複製給返回值變量
21             returnValue = GetParentNode(p->rchild, value,returnValue);
22         } else {////若是value只小於p節點值,則遞歸左孩子
23             //直到找到value的父節點複製給返回值變量
24             returnValue = GetParentNode(p->lchild, value,returnValue);
25         }
26     }
27 
28     return returnValue;
29 
30 }
31 
32 /**
33  *獲取傳入的節點的父節點
34  */
35 TNode<T>* GetParentNode(const T &value,TNode<T> *returnValue) {
36     return this->GetParentNode(root, value,returnValue);
37 }

六、二分查找

代碼分析:

 1 /**
 2  * 在以T爲根節點的樹中搜索值爲value的節點
 3  */
 4 TNode<T>* SearchTree(TNode<T>* &t, const T &value) {
 5     //判斷t節點是否爲空
 6     while (t) {
 7         //若是節點值等於value,則代表已經找到目標節點
 8         if (t->data == value) {
 9             return t;
10         } else if (value > t->data) {//若是value大於t節點值,則遞歸查詢右子樹
11             return SearchTree(t->rchild, value);
12         } else {//若是value小於t節點值,則遞歸查詢左子樹
13             return SearchTree(t->lchild, value);
14         }
15     }
16     return t;
17 }

 動畫演示:

七、插入節點

代碼分析:

 1 /**
 2  *插入一個節點到目標樹中
 3  */
 4 void Insert(const T &value, TNode<T>* &t) {
 5     //若是目標樹爲空,則新new一個根節點
 6     if (t == 0) {
 7         //新建立一個節點,並把value設置爲節點值
 8         t = new TNode<T>(value);
 9     } else if (value < t->data) {//若是value值小於t節點值
10         //遞歸左子樹插入函數,直到找到節點插入
11         this->Insert(value, t->lchild);
12     } else if (value > t->data) {//若是value值大於t節點值
13         //遞歸右子樹插入函數,直到找到節點插入
14         this->Insert(value, t->rchild);
15     }
16 }
17 /**
18  *插入一個節點到根節點爲root的樹中
19  */
20 void Insert(const T &value) {
21     this->Insert(value, root);
22 }

動畫演示

八、刪除節點

代碼分析:

  1 /**
  2  *根據節點值刪除節點信息
  3  */
  4 void Delete(const T &value) {
  5     Delete(value, root);
  6 }
  7 /**
  8  *根據節點值刪除以傳入t爲根節點樹節點信息
  9  */
 10 void Delete(const T &value, TNode<T>* &t) {
 11     //判斷是否t爲空null
 12     if (t) {
 13         //經過二分查找定位value所在的節點
 14         TNode<T> *p = this->SearchTree(t, value);
 15         //中間變量,用於待刪除節點左右子樹都不爲空的狀況下
 16         TNode<T> *q = p;
 17         if (p) {
 18             //若是p節點的左右孩子都不爲空,則根據二叉樹定義
 19             //必須在子右子樹中找到最新節點做爲新節點
 20             //當左右子樹都爲空狀況下,右子樹最小節點就是樹(中序遍歷)節點的後繼節點
 21             if (p->lchild != 0 && p->rchild != 0) {
 22                 //獲取右子樹中最小的節點
 23                 q = this->GetMin(p->rchild);
 24             }
 25             //獲取資源當即放入管理對象(參考Effective C++裏邊條款13)
 26             //tr1::shared_ptr(引用計數智慧指針)管理對象比auto_ptr更強大
 27             //若是p節點的左右子樹都不爲空,則釋放p節點子右子樹的最小節點
 28             //改變p節點的值便可
 29             auto_ptr<TNode<T> > new_ptr(q);
 30 
 31             TNode<T> *parent = 0;
 32             TNode<T> *returnValue;
 33             //刪除葉子節點(節點左右孩子都爲空)
 34             if (p->lchild == 0 && p->rchild == 0) {
 35                 //若是p節點和傳入的根節點相等
 36                 if (t == p) {
 37                     //直接設置t爲空
 38                     t = 0;
 39                 } else {
 40                     //獲取p節點的父節點
 41                     parent = this->GetParentNode(t, p->data,returnValue);
 42 
 43                     //若是父節點的左孩子等於p節點
 44                     if (parent->lchild == p) {
 45                         //設置父節點的左孩子等於空
 46                         parent->lchild = 0;
 47                     } else {//若是父節點的右孩子等於p節點
 48                         //設置父節點的右孩子等於空
 49                         parent->rchild = 0;
 50                     }
 51                 }
 52 
 53             } else if (p->rchild == 0) {//刪除節點p右孩子爲空,左孩子有節點
 54                 //若是p節點和傳入的根節點相等
 55                 if (t == p) {
 56                     //直接設置t節點等於左孩子
 57                     t = t->lchild;
 58                 } else {
 59                     //獲取p節點的父節點
 60                     parent = this->GetParentNode(t, p->data,returnValue);
 61                     //若是父節點的左孩子等於p節點
 62                     if (parent->lchild == p) {
 63                         //設置父節點左孩子等於p節點左孩子
 64                         parent->lchild = p->lchild;
 65                     } else {//若是父節點的右孩子等於p節點
 66                         //設置父節點右孩子等於p節點左孩子
 67                         parent->rchild = p->lchild;
 68                     }
 69                 }
 70 
 71             } else if (p->lchild == 0) {//刪除節點p左孩子爲空,右孩子有節點
 72                 //若是p節點和傳入的根節點相等
 73                 if (t == p) {
 74                     //直接設置t節點等於右孩子
 75                     t = t->rchild;
 76                 } else {
 77                     //獲取p節點的父節點
 78                     parent = this->GetParentNode(t, p->data,returnValue);
 79                     //若是父節點的右孩子等於p節點
 80                     if (parent->rchild == p) {
 81                         //設置父節點右孩子等於p節點右孩子
 82                         parent->rchild = p->rchild;
 83                     } else {//若是父節點的左孩子等於p節點
 84                         //設置父節點右孩子等於p節點右孩子
 85                         parent->lchild = p->rchild;
 86                     }
 87                 }
 88             } else {//刪除節點p左右都有孩子
 89                 //獲取q節點的父節點
 90                 parent = this->GetParentNode(t,q->data,returnValue);
 91                 //設置p節點值等於q節點值
 92                 p->data = q->data;
 93                 //若是q的父節點等於p
 94                 if (parent == p) {
 95                     //設置q節點的父節點右孩子爲q節點右孩子
 96                     parent->rchild = q->rchild;
 97                 } else {//
 98                     //設置q節點的父節點左孩子爲q節點右孩子
 99                     parent->lchild = q->rchild;
100                 }
101 
102             }
103         }
104     }
105 }

動畫演示

九、獲取目標節點後繼節點(中序遍歷) 

 1 /**
 2  *在傳入p的樹中找出節點值爲value的後繼節點方法
 3  */
 4 TNode<T>* TreeSuccessor(TNode<T> *p,const T &value,TNode<T> *returnValue){
 5     //若是t節點非空
 6     if(p){
 7         //傳入p樹和節點值value找到對應的節點
 8         TNode<T> *t = this->SearchTree(p, value);
 9         //若是節點右子樹不爲空
10         if(t->rchild != 0){
11             //直接獲取右子樹中最小節點,便是節點的後繼節點
12             returnValue = this->GetMin(t->rchild);
13         }else{
14             //獲取目標節點父節點
15             TNode<T> *parent = this->GetParentNode(t->data,returnValue);
16 
17             //若是父節點不爲空而且t節點等於父節點的右節點,這一個文字不太好描述,請參照圖
18             while(parent && t == parent->rchild){
19                 //父節點賦值給t節點
20                 t = parent;
21                 //獲取父節點的父節點(目標節點爺爺)
22                 parent = this->GetParentNode(parent->data,returnValue);
23             }
24             //找到後繼節點賦值給返回變量
25             returnValue = parent;
26         }
27     }
28 
29     return returnValue;
30 }
31 /**
32  *在以root爲根節點中找出節點值爲value的後繼節點方法
33  */
34 TNode<T>* TreeSuccessor(const T &value,TNode<T> *returnValue){
35     return TreeSuccessor(root,value,returnValue);
36 }

 以下圖:

  

十、先序、中序、後序遞歸和非的遞歸遍歷,更詳細的請參考上一篇文章,爲何在這裏有展現一遍,我堅信在複雜的東西,多動手寫幾回都能很好的理解

  1 /**
  2  *前序非遞歸(利用棧)遍歷二叉樹
  3  *前序遍歷的規則:根左右
  4  *非遞歸遍歷樹會常常當作面試題,考察面試者的編程能力
  5  *防止下次被鄙視,應該深刻理解而且動手在紙寫出來
  6  */
  7 void PreOrderTraverse() {
  8     //申明一個棧對象
  9     stack<TNode<T>*> s;
 10     //t首先指向根節點
 11     TNode<T> *t = root;
 12     //壓入一個空指針,做爲判斷條件
 13     s.push(0);
 14 
 15     //若是t所值節點非空
 16     while (t != 0) {
 17         //直接訪問根節點
 18         std::cout << (&t->data) << " ";
 19 
 20         //右孩子指針爲非空
 21         if (t->rchild != 0) {
 22             //入棧右孩子指針
 23             s.push(t->rchild);
 24         }
 25 
 26         //左孩子指針爲非空
 27         if (t->lchild != 0) {
 28             //直接指向其左孩子
 29             t = t->lchild;
 30         } else {//左孩子指針爲空
 31             //獲取棧頂元素(右孩子指針)
 32             t = s.top();
 33             //清楚棧頂元素
 34             s.pop();
 35         }
 36 
 37     }
 38 }
 39 
 40 /**
 41  *中序非遞歸(利用棧)遍歷二叉樹
 42  *前序遍歷的規則:左根右
 43  */
 44 void InOrderTraverse() {
 45     //申明一個棧對象
 46     stack<TNode<T>*> s;
 47     //t首先指向根節點
 48     TNode<T>* t = root;
 49 
 50     //節點不爲空或者棧對象不爲空,都進入循環
 51     while (t != 0 || !s.empty()) {
 52         //若是t節點非空
 53         if (t) {
 54             //入棧t節點
 55             s.push(t);
 56             //t節點指向其左孩子
 57             t = t->lchild;
 58         } else {
 59             //獲取棧頂元素(左孩子指針)
 60             t = s.top();
 61             //清楚棧頂元素
 62             s.pop();
 63             //直接訪問t節點
 64             std::cout << (&t->data) << " ";
 65             //t節點指向其右孩子
 66             t = t->rchild;
 67         }
 68     }
 69 }
 70 /**
 71  *後序非遞歸(利用棧)遍歷二叉樹
 72  *前序遍歷的規則:左右根
 73  */
 74 void PostOrderTraverse() {
 75     //申明一個棧對象
 76     stack<TNode<T>*> s;
 77     //t首先指向根節點
 78     TNode<T>* t = root;
 79     //申請中間變量,用作判斷標識
 80     TNode<T>* r;
 81     //節點不爲空或者棧對象不爲空,都進入循環
 82     while (t != 0 || !s.empty()) {
 83         //若是t節點非空
 84         if (t) {
 85             //入棧t節點
 86             s.push(t);
 87             //t節點指向其左孩子
 88             t = t->lchild;
 89         } else {
 90             //獲取棧頂元素(左孩子指針)
 91             t = s.top();
 92             //判斷t的右子樹是否存在而且沒有訪問過
 93             if (t->rchild && t->rchild != r) {
 94                 //t節點指向其右孩子
 95                 t = t->rchild;
 96                 //入棧t節點
 97                 s.push(t);
 98                 //t節點指向其左孩子
 99                 t = t->lchild;
100             } else {
101                 //獲取棧頂元素(左孩子指針)
102                 t = s.top();
103                 //清楚棧頂元素
104                 s.pop();
105                 //直接訪問t節點
106                 std::cout << (&t->data) << " ";
107                 //設置已經訪問過的節點,防止屢次訪問(右孩子指針)
108                 r = t;
109                 t = 0;
110             }
111         }
112     }
113 }
114 /**
115  * 根據模式遞歸遍歷二叉樹
116  * 下面代碼相對比較簡單,只要記住遍歷樹的規則而且弄一點遞歸,均可以寫出來
117  * 若是在面試中實在寫不出來非遞歸方式,能夠寫一個遞歸版本,也許能夠爭取一個好的工做
118  */
119 void OrderTraverse(const TNode<T>* t, Style mode) {
120     if (t) {
121         //先序遍歷二叉樹:根左右
122         if (mode == Pre) {
123             //直接訪問t節點
124             std::cout << (&t->data) << " ";
125             //遞歸遍歷左子樹
126             this->OrderTraverse(t->lchild, mode);
127             //遞歸遍歷右子樹
128             this->OrderTraverse(t->rchild, mode);
129         }
130         //中序遍歷二叉樹:左根右
131         if (mode == In) {
132             //遞歸遍歷左子樹
133             this->OrderTraverse(t->lchild, mode);
134             //直接訪問t節點
135             std::cout << (&t->data) << " ";
136             //遞歸遍歷右子樹
137             this->OrderTraverse(t->rchild, mode);
138         }
139         //後序遍歷二叉樹:左右根
140         if (mode == Post) {
141             //遞歸遍歷左子樹
142             this->OrderTraverse(t->lchild, mode);
143             //遞歸遍歷右子樹
144             this->OrderTraverse(t->rchild, mode);
145             //直接訪問t節點
146             std::cout << (&t->data) << " ";
147         }
148     }
149 }

十一、按層級遍歷樹

 1 /**
 2  *按層級從左到右遍歷樹
 3  */
 4 void LevelOrderTraverse(){
 5     //聲明一個隊列隊形
 6     queue<TNode<T>* > q;
 7     //聲明變量a,t;並把root賦值給t
 8     TNode<T> *a,*t = root;
 9     //若t節點非空
10     if(t){
11         //t節點入隊列
12         q.push(t);
13         //若是隊列不爲空
14         while(!q.empty()){
15             //獲取隊列頭結點
16             a = q.front();
17             //數據隊形出隊列
18             q.pop();
19             //直接訪問隊列頭結點值
20             std::cout<<a->data<<" ";
21             //若a節點左子樹不爲空
22             if(a->lchild){
23                 //左子樹入隊列q
24                 q.push(a->lchild);
25             }
26             //若a節點右子樹不爲空
27             if(a->rchild){
28                 //右子樹入隊列q
29                 q.push(a->rchild);
30             }
31         }
32     }
33 }

十二、運行結果,因爲在虛擬機中打中文,實在太痛苦,弄一點英文裝下B 

測試代碼以下:

 1 void test() {
 2     Insert(5);
 3     Insert(3);
 4     Insert(4);
 5     Insert(6);
 6     Insert(2);
 7     Insert(1);
 8     Insert(10);
 9     Insert(9);
10     Insert(8);
11     Insert(11);
12     Insert(12);
13     std::cout << "create tree success" << std::endl;
14 
15     std::cout << "create tree after is null ? ";
16     std::cout << boolalpha << this->IsEmpty();
17 
18     std::cout << std::endl;
19     std::cout << "calculated depth of the tree begins" << std::endl;
20     std::cout << "tree is dept = " << this->GetTreeDept(this->GetRoot());
21     std::cout << std::endl;
22     std::cout << "calculated depth of the tree end" << std::endl;
23 
24     std::cout << std::endl;
25     std::cout << "recursion--------------------begin" << std::endl;
26     std::cout << "pre order traverse:";
27     OrderTraverse(GetRoot(), Pre);
28     std::cout << endl;
29 
30     std::cout << "in order traverse:";
31     OrderTraverse(GetRoot(), In);
32     std::cout << endl;
33 
34     std::cout << "post order traverse:";
35     OrderTraverse(GetRoot(), Post);
36     std::cout << endl;
37     std::cout << "recursion--------------------end" << std::endl;
38 
39     std::cout << "get parent node--------------begin" << std::endl;
40     TNode<T> *returnValue;
41     TNode<T> *node = GetParentNode(5,returnValue);
42     if (node) {
43         std::cout << "node=" << node->data;
44     }
45     std::cout << "get parent node--------------end" << std::endl;
46     std::cout << "delete-----------------------begin" << std::endl;
47     Delete(5);
48     std::cout << "delete-----------------------end" << std::endl;
49 
50     std::cout << "recursion--------------------begin" << std::endl;
51     std::cout << "in order traverse:";
52     OrderTraverse(GetRoot(), In);
53     std::cout << endl;
54     std::cout << "recursion--------------------end" << std::endl;
55     std::cout<<"tree max:"<<GetMax(GetRoot())->data<<std::endl;
56     std::cout<<"tree min:"<<GetMin(GetRoot())->data<<std::endl;
57 }
View Code

結果以下圖

十一、完整代碼

TNode.h

 1 /*
 2  * TLNode.h
 3  *
 4  *  Created on: 2013-7-6
 5  *      Author: sunysen
 6  */
 7 
 8 #ifndef TLNODE_H_
 9 #define TLNODE_H_
10 template <class T>
11 class TNode{
12 public:
13     T data;
14     TNode *rchild,*lchild;
15     TNode(T value):data(value),rchild(0),lchild(0){}
16 };
17 
18 #endif /* TLNODE_H_ */
View Code

 BSTree.h

  1 /*
  2  * LinkTree.h
  3  *  Created on: 2013-7-6
  4  *      Author: sunysen
  5  */
  6 
  7 #ifndef BSTREE_H_
  8 #define BSTREE_H_
  9 #include "core/common/Common.h"
 10 #include "core/node/TNode.h"
 11 //獲取樹最大最小值模式
 12 enum Mode {
 13     Max, Min
 14 };
 15 //樹的三種遍歷方式
 16 enum ORDER_MODE {
 17     Pre, In, Post
 18 };
 19 template<class T>
 20 class BSTree {
 21 private:
 22     TNode<T> *root;//樹的根節點
 23 public:
 24     /**
 25      * 構造函數初始化樹根節點
 26      */
 27     BSTree() :
 28             root(0) {
 29     }
 30     /**
 31      *析構行數釋放全部構造的資源
 32      */
 33     ~BSTree(){
 34         Clear();
 35     }
 36     /**
 37      * 若樹爲空,則返回true;不然返回false
 38      */
 39     bool IsEmpty() {
 40         return root == 0;
 41     }
 42 
 43     /**
 44      * 以傳入節點爲基礎計算樹的深度
 45      */
 46     int GetTreeDept(const TNode<T> *t) {
 47         int i, j;
 48         if (t == 0) {
 49             return 0;
 50         } else {
 51             //遞歸計算左子樹的深度
 52             i = this->GetTreeDept(t->lchild);
 53             //遞歸計算右子樹的深度
 54             j = this->GetTreeDept(t->rchild);
 55         }
 56 
 57         //t的深度爲其左右子樹中深度中的大者加1
 58         return i > j ? i + 1 : j + 1;
 59     }
 60     /**
 61      *清空樹的全部節點
 62      */
 63     void Clear() {
 64         Clear(root);
 65     }
 66 
 67     /**
 68      *根據節點遞歸清空樹
 69      */
 70     void Clear(TNode<T>* t) {
 71         //判斷指針是否爲空
 72         if (t) {
 73             //獲取資源當即放入管理對象(參考Effective C++裏邊條款13)
 74             //tr1::shared_ptr(引用計數智慧指針)管理對象比auto_ptr更強大
 75             std::auto_ptr<TNode<T> > new_ptr(t);
 76 
 77             //遞歸清空右子樹
 78             Clear(new_ptr->rchild);
 79 
 80             //遞歸清空左子樹
 81             Clear(new_ptr->lchild);
 82         }
 83 
 84         //清空樹的根節點
 85         t = 0;
 86     }
 87 
 88     /**
 89      *獲取樹的最大節點
 90      */
 91     TNode<T>* GetMax(TNode<T>* t) const {
 92         //判斷數節點是否爲空
 93         if (t) {
 94             //根據二叉樹特性,最大值必定在右子樹;
 95             //循環右子樹,直到葉子節點
 96             while (t->rchild) {
 97                 //指向下一個節點
 98                 t = t->rchild;
 99             }
100         }
101         //返回找到最大節點
102         return t;
103     }
104     /**
105      *獲取樹的最小節點
106      */
107     TNode<T>* GetMin(TNode<T>* t) const {
108         //判斷數節點是否爲空
109         if (t) {
110             //根據二叉樹特性,最大值必定在左子樹;
111             //循環左子樹,直到葉子節點
112             while (t->lchild) {
113                 //指向下一個節點
114                 t = t->lchild;
115             }
116         }
117         //返回找到最小節點
118         return t;
119     }
120     /**
121      *根據模式類型查找樹的最大值或者最小值
122      */
123     TNode<T>* GetNode(Mode mode) const {
124         //t指向根節點
125         TNode<T>* t = root;
126         //判斷數節點是否爲空
127         if (t) {
128             if (mode == Min) {
129                 //根據二叉樹特性,最大值必定在左子樹;
130                 //循環左子樹,直到葉子節點
131                 while (t->lchild) {
132                     //指向左子樹下一個節點
133                     t = t->lchild;
134                 }
135             } else if (mode == Max) {
136                 //根據二叉樹特性,最大值必定在右子樹;
137                 //循環右子樹,直到葉子節點
138                 while (t->rchild) {
139                     //指向右子樹下一個節點
140                     t = t->rchild;
141                 }
142             }
143         }
144         //返回找到節點
145         return t;
146     }
147     /**
148      *獲取傳入的節點從傳入樹p中找到它的父節點
149      */
150     TNode<T>* GetParentNode(TNode<T> *p, const T &value,TNode<T> *returnValue) {
151         //p節點存在而且傳入的值不是根節點值
152         if (p && p->data == value) {
153             return 0;
154         }
155 
156         //用二分查找定位值value所在節點
157         TNode<T> *t = this->SearchTree(p, value);
158 
159         //判斷t和p都不爲空
160         if (t && p) {
161             //若是value的節點等於p節點左孩子或者右孩子,p就是value的節點父親
162             if (p->lchild == t || p->rchild == t) {
163                 //賦值p節點給返回值變量
164                 returnValue = p;
165             } else if (value > p->data) {//若是value只大於p節點值,則遞歸右孩子
166                 //直到找到value的父節點複製給返回值變量
167                 returnValue = GetParentNode(p->rchild, value,returnValue);
168             } else {////若是value只小於p節點值,則遞歸左孩子
169                 //直到找到value的父節點複製給返回值變量
170                 returnValue = GetParentNode(p->lchild, value,returnValue);
171             }
172         }
173 
174         return returnValue;
175 
176     }
177 
178     /**
179      *獲取傳入的節點的父節點
180      */
181     TNode<T>* GetParentNode(const T &value,TNode<T> *returnValue) {
182         return this->GetParentNode(root, value,returnValue);
183     }
184 
185     /**
186      * 在以T爲根節點的樹中搜索值爲value的節點
187      */
188     TNode<T>* SearchTree(TNode<T>* &t, const T &value) {
189         //判斷t節點是否爲空
190         while (t) {
191             //若是節點值等於value,則代表已經找到目標節點
192             if (t->data == value) {
193                 return t;
194             } else if (value > t->data) {//若是value大於t節點值,則遞歸查詢右子樹
195                 return SearchTree(t->rchild, value);
196             } else {//若是value小於t節點值,則遞歸查詢左子樹
197                 return SearchTree(t->lchild, value);
198             }
199         }
200         return t;
201     }
202 
203     /**
204      *插入一個節點到目標樹中
205      */
206     void Insert(const T &value, TNode<T>* &t) {
207         //若是目標樹爲空,則新new一個根節點
208         if (t == 0) {
209             //新建立一個節點,並把value設置爲節點值
210             t = new TNode<T>(value);
211         } else if (value < t->data) {//若是value值小於t節點值
212             //遞歸左子樹插入函數,直到找到節點插入
213             this->Insert(value, t->lchild);
214         } else if (value > t->data) {//若是value值大於t節點值
215             //遞歸右子樹插入函數,直到找到節點插入
216             this->Insert(value, t->rchild);
217         }
218     }
219     /**
220      *插入一個節點到根節點爲root的樹中
221      */
222     void Insert(const T &value) {
223         this->Insert(value, root);
224     }
225 
226     /**
227      *根據節點值刪除節點信息
228      */
229     void Delete(const T &value) {
230         Delete(value, root);
231     }
232     /**
233      *根據節點值刪除以傳入t爲根節點樹節點信息
234      */
235     void Delete(const T &value, TNode<T>* &t) {
236         //判斷是否t爲空null
237         if (t) {
238             //經過二分查找定位value所在的節點
239             TNode<T> *p = this->SearchTree(t, value);
240             //中間變量,用於待刪除節點左右子樹都不爲空的狀況下
241             TNode<T> *q = p;
242             if (p) {
243                 //若是p節點的左右孩子都不爲空,則根據二叉樹定義
244                 //必須在子右子樹中找到最新節點做爲新節點
245                 if (p->lchild != 0 && p->rchild != 0) {
246                     //獲取右子樹中最小的節點
247                     q = this->GetMin(p->rchild);
248                 }
249                 //獲取資源當即放入管理對象(參考Effective C++裏邊條款13)
250                 //tr1::shared_ptr(引用計數智慧指針)管理對象比auto_ptr更強大
251                 //若是p節點的左右子樹都不爲空,則釋放p節點子右子樹的最小節點
252                 //改變p節點的值,便可刪除節點
253                 auto_ptr<TNode<T> > new_ptr(q);
254 
255                 TNode<T> *parent = 0;
256                 TNode<T> *returnValue;
257                 //刪除葉子節點(節點左右孩子都爲空)
258                 if (p->lchild == 0 && p->rchild == 0) {
259                     //若是p節點和傳入的根節點相等
260                     if (t == p) {
261                         //直接設置t爲空
262                         t = 0;
263                     } else {
264                         //獲取p節點的父節點
265                         parent = this->GetParentNode(t, p->data,returnValue);
266 
267                         //若是父節點的左孩子等於p節點
268                         if (parent->lchild == p) {
269                             //設置父節點的左孩子等於空
270                             parent->lchild = 0;
271                         } else {//若是父節點的右孩子等於p節點
272                             //設置父節點的右孩子等於空
273                             parent->rchild = 0;
274                         }
275                     }
276 
277                 } else if (p->rchild == 0) {//刪除節點p右孩子爲空,左孩子有節點
278                     //若是p節點和傳入的根節點相等
279                     if (t == p) {
280                         //直接設置t節點等於左孩子
281                         t = t->lchild;
282                     } else {
283                         //獲取p節點的父節點
284                         parent = this->GetParentNode(t, p->data,returnValue);
285                         //若是父節點的左孩子等於p節點
286                         if (parent->lchild == p) {
287                             //設置父節點左孩子等於p節點左孩子
288                             parent->lchild = p->lchild;
289                         } else {//若是父節點的右孩子等於p節點
290                             //設置父節點右孩子等於p節點左孩子
291                             parent->rchild = p->lchild;
292                         }
293                     }
294 
295                 } else if (p->lchild == 0) {//刪除節點p左孩子爲空,右孩子有節點
296                     //若是p節點和傳入的根節點相等
297                     if (t == p) {
298                         //直接設置t節點等於右孩子
299                         t = t->rchild;
300                     } else {
301                         //獲取p節點的父節點
302                         parent = this->GetParentNode(t, p->data,returnValue);
303                         //若是父節點的右孩子等於p節點
304                         if (parent->rchild == p) {
305                             //設置父節點右孩子等於p節點右孩子
306                             parent->rchild = p->rchild;
307                         } else {//若是父節點的左孩子等於p節點
308                             //設置父節點右孩子等於p節點右孩子
309                             parent->lchild = p->rchild;
310                         }
311                     }
312                 } else {//刪除節點p左右都有孩子
313                     //獲取q節點的父節點
314                     parent = this->GetParentNode(t,q->data,returnValue);
315                     //設置p節點值等於q節點值
316                     p->data = q->data;
317                     //若是q的父節點等於p
318                     if (parent == p) {
319                         //設置q節點的父節點右孩子爲q節點右孩子
320                         parent->rchild = q->rchild;
321                     } else {//
322                         //設置q節點的父節點左孩子爲q節點右孩子
323                         parent->lchild = q->rchild;
324                     }
325 
326                 }
327             }
328         }
329     }
330 
331     /**
332      *前序非遞歸(利用棧)遍歷二叉樹
333      *前序遍歷的規則:根左右
334      *非遞歸遍歷樹會常常當作面試題,考察面試者的編程能力
335      *防止下次被鄙視,應該深刻理解而且動手在紙寫出來
336      */
337     void PreOrderTraverse() {
338         //申明一個棧對象
339         stack<TNode<T>*> s;
340         //t首先指向根節點
341         TNode<T> *t = root;
342         //壓入一個空指針,做爲判斷條件
343         s.push(0);
344 
345         //若是t所值節點非空
346         while (t != 0) {
347             //直接訪問根節點
348             std::cout << (&t->data) << " ";
349 
350             //右孩子指針爲非空
351             if (t->rchild != 0) {
352                 //入棧右孩子指針
353                 s.push(t->rchild);
354             }
355 
356             //左孩子指針爲非空
357             if (t->lchild != 0) {
358                 //直接指向其左孩子
359                 t = t->lchild;
360             } else {//左孩子指針爲空
361                 //獲取棧頂元素(右孩子指針)
362                 t = s.top();
363                 //清楚棧頂元素
364                 s.pop();
365             }
366 
367         }
368     }
369 
370     /**
371      *中序非遞歸(利用棧)遍歷二叉樹
372      *前序遍歷的規則:左根右
373      */
374     void InOrderTraverse() {
375         //申明一個棧對象
376         stack<TNode<T>*> s;
377         //t首先指向根節點
378         TNode<T>* t = root;
379 
380         //節點不爲空或者棧對象不爲空,都進入循環
381         while (t != 0 || !s.empty()) {
382             //若是t節點非空
383             if (t) {
384                 //入棧t節點
385                 s.push(t);
386                 //t節點指向其左孩子
387                 t = t->lchild;
388             } else {
389                 //獲取棧頂元素(左孩子指針)
390                 t = s.top();
391                 //清楚棧頂元素
392                 s.pop();
393                 //直接訪問t節點
394                 std::cout << (&t->data) << " ";
395                 //t節點指向其右孩子
396                 t = t->rchild;
397             }
398         }
399     }
400     /**
401      *後序非遞歸(利用棧)遍歷二叉樹
402      *前序遍歷的規則:左右根
403      */
404     void PostOrderTraverse() {
405         //申明一個棧對象
406         stack<TNode<T>*> s;
407         //t首先指向根節點
408         TNode<T>* t = root;
409         //申請中間變量,用作判斷標識
410         TNode<T>* r;
411         //節點不爲空或者棧對象不爲空,都進入循環
412         while (t != 0 || !s.empty()) {
413             //若是t節點非空
414             if (t) {
415                 //入棧t節點
416                 s.push(t);
417                 //t節點指向其左孩子
418                 t = t->lchild;
419             } else {
420                 //獲取棧頂元素(左孩子指針)
421                 t = s.top();
422                 //判斷t的右子樹是否存在而且沒有訪問過
423                 if (t->rchild && t->rchild != r) {
424                     //t節點指向其右孩子
425                     t = t->rchild;
426                     //入棧t節點
427                     s.push(t);
428                     //t節點指向其左孩子
429                     t = t->lchild;
430                 } else {
431                     //獲取棧頂元素(左孩子指針)
432                     t = s.top();
433                     //清楚棧頂元素
434                     s.pop();
435                     //直接訪問t節點
436                     std::cout << (&t->data) << " ";
437                     //設置已經訪問過的節點,防止屢次訪問(右孩子指針)
438                     r = t;
439                     t = 0;
440                 }
441             }
442         }
443     }
444     /**
445      * 根據模式遞歸遍歷二叉樹
446      * 下面代碼相對比較簡單,只要記住遍歷樹的規則而且弄一點遞歸,均可以寫出來
447      * 若是在面試中實在寫不出來非遞歸方式,能夠寫一個遞歸版本,也許能夠爭取一個好的工做
448      */
449     void OrderTraverse(const TNode<T>* t, Style mode) {
450         if (t) {
451             //先序遍歷二叉樹:根左右
452             if (mode == Pre) {
453                 //直接訪問t節點
454                 std::cout << (&t->data) << " ";
455                 //遞歸遍歷左子樹
456                 this->OrderTraverse(t->lchild, mode);
457                 //遞歸遍歷右子樹
458                 this->OrderTraverse(t->rchild, mode);
459             }
460             //中序遍歷二叉樹:左根右
461             if (mode == In) {
462                 //遞歸遍歷左子樹
463                 this->OrderTraverse(t->lchild, mode);
464                 //直接訪問t節點
465                 std::cout << (&t->data) << " ";
466                 //遞歸遍歷右子樹
467                 this->OrderTraverse(t->rchild, mode);
468             }
469             //後序遍歷二叉樹:左右根
470             if (mode == Post) {
471                 //遞歸遍歷左子樹
472                 this->OrderTraverse(t->lchild, mode);
473                 //遞歸遍歷右子樹
474                 this->OrderTraverse(t->rchild, mode);
475                 //直接訪問t節點
476                 std::cout << (&t->data) << " ";
477             }
478         }
479     }
480 
481 
482     TNode<T>* GetRoot() {
483         return root;
484     }
485 
486     void test() {
487         Insert(5);
488         Insert(3);
489         Insert(4);
490         Insert(6);
491         Insert(2);
492         Insert(1);
493         Insert(10);
494         Insert(9);
495         Insert(8);
496         Insert(11);
497         Insert(12);
498         std::cout << "create tree success" << std::endl;
499 
500         std::cout << "create tree after is null ? ";
501         std::cout << boolalpha << this->IsEmpty();
502 
503         std::cout << std::endl;
504         std::cout << "calculated depth of the tree begins" << std::endl;
505         std::cout << "tree is dept = " << this->GetTreeDept(this->GetRoot());
506         std::cout << std::endl;
507         std::cout << "calculated depth of the tree end" << std::endl;
508 
509         std::cout << std::endl;
510         std::cout << "recursion--------------------begin" << std::endl;
511         std::cout << "pre order traverse:";
512         OrderTraverse(GetRoot(), Pre);
513         std::cout << endl;
514 
515         std::cout << "in order traverse:";
516         OrderTraverse(GetRoot(), In);
517         std::cout << endl;
518 
519         std::cout << "post order traverse:";
520         OrderTraverse(GetRoot(), Post);
521         std::cout << endl;
522         std::cout << "recursion--------------------end" << std::endl;
523 
524         std::cout << "get parent node--------------begin" << std::endl;
525         TNode<T> *returnValue;
526         TNode<T> *node = GetParentNode(5,returnValue);
527         if (node) {
528             std::cout << "node=" << node->data;
529         }
530         std::cout << "get parent node--------------end" << std::endl;
531         std::cout << "delete-----------------------begin" << std::endl;
532         Delete(5);
533         std::cout << "delete-----------------------end" << std::endl;
534 
535         std::cout << "recursion--------------------begin" << std::endl;
536         std::cout << "in order traverse:";
537         OrderTraverse(GetRoot(), In);
538         std::cout << endl;
539         std::cout << "recursion--------------------end" << std::endl;
540         std::cout<<"tree max:"<<GetMax(GetRoot())->data<<std::endl;
541         std::cout<<"tree min:"<<GetMin(GetRoot())->data<<std::endl;
542     }
543 
544 };
545 
546 #endif /* BSTREE_H_ */
View Code

五:環境

一、運行環境:Ubuntu 10.04 LTS+VMware8.0.4+gcc4.4.3;

二、開發工具:Eclipse+make

六:題記

一、上面的代碼不免有bug,若是你發現代碼寫的有問題,請你幫忙指出,讓咱們一塊兒進步,讓代碼變的更漂亮和更健壯;

二、我本身能手動寫上面代碼,離不開郝斌、高一凡、侯捷、嚴蔚敏等老師的書籍和視頻指導,在這裏感謝他們;

三、鼓勵本身能堅持把更多數據結構方面的知識寫出來,讓本身掌握更深入,也順便冒充下"小牛";

 四、全部動畫都在網上找的,感謝製做作動畫的朋友,這樣好的動畫比圖片更便於你們理解複雜的內容;

 

歡迎繼續閱讀「啓迪思惟:數據結構和算法」系列

相關文章
相關標籤/搜索