启迪思维:二叉树

很多初级点的程序员会认为树结构无用论,也有初级程序员仅仅以为只有面试才会用到,还有自认为实际工作用不到(我身边工作好几年程序员懂树结构也没有几个),其实归根到底还是不清楚树的实际用途,下面分享我参加培训时候一个小尴尬。

 

因为项目数据量很大(有很多表数据量都上亿的),对写sql能力要求很高,项目组会经常组织些数据库方面的培训,前段时间又参加公司一个SQL原理分析的一个培训,在培训中讲师问“为什么SQL走索引查询速度很快呢?”,我直接大声说“索引底层数据结构是B树,查询的时候用二分查找”,结果整个大房间就我一个人声音,所有同事都看过来,场面有点尴尬。

 

上一篇文章分析树基本概念、名词解释、树三种遍历方式,今天继续来看二叉树各种名词概率,看这些名词概念,肯定是各种不爽,大概浏览下知道怎么回事就ok

 

一:概念,下面这些内容摘自维基百科

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有个结点;深度为k的二叉树至多有个结点;对任何一棵二叉树T,如果其终端结点数为,度为2的结点数为,则。

树和二叉树的三个主要差别:

树的结点个数至少为1,而二叉树的结点个数可以为0

树中结点的最大度数没有限制,而二叉树结点的最大度数为2

树的结点无左、右之分,而二叉树的结点有左、右之分。

完全二叉树和满二叉树

满二叉树:一棵深度为k,且有个节点成为满二叉树

完全二叉树:深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中序号为1n的节点对应时,称之为完全二叉树

 

二:示例图

 

三:动画图

四:代码分析

 1、是否为空

1 /**
2  * 若树为空,则返回true;否则返回false
3  */
4 bool IsEmpty() {
5     return root == 0;
6 }

2、计算树的深度

 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 }

3、清空树的节点

 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 }

4、获取树最大节点和最小节点

 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 }

5、获取传入节点父节点

 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 }

6、二分查找

代码分析:

 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 }

 动画演示:

7、插入节点

代码分析:

 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 }

动画演示

8、删除节点

代码分析:

  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 }

动画演示

9、获取目标节点后继节点(中序遍历) 

 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 }

 如下图:

  

10、先序、中序、后序递归和非的递归遍历,更详细的请参考上一篇文章,为什么在这里有展示一遍,我坚信在复杂的东西,多动手写几次都能很好的理解

  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 }

11、按层级遍历树

 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 }

12、运行结果,由于在虚拟机中打中文,实在太痛苦,弄一点英文装下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

结果如下图

11、完整代码

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

五:环境

1、运行环境:Ubuntu 10.04 LTS+VMware8.0.4+gcc4.4.3;

2、开发工具:Eclipse+make

六:题记

1、上面的代码难免有bug,如果你发现代码写的有问题,请你帮忙指出,让我们一起进步,让代码变的更漂亮和更健壮;

2、我自己能手动写上面代码,离不开郝斌、高一凡、侯捷、严蔚敏等老师的书籍和视频指导,在这里感谢他们;

3、鼓励自己能坚持把更多数据结构方面的知识写出来,让自己掌握更深刻,也顺便冒充下"小牛";

 4、所有动画都在网上找的,感谢制作做动画的朋友,这样好的动画比图片更便于大家理解复杂的内容;

 

欢迎继续阅读“启迪思维:数据结构和算法”系列

 

 

 

 

 

posted @ 2013-08-12 15:42  sunysen  阅读(1676)  评论(12编辑  收藏  举报