BinarySearchTree示例——C++模板实现

数据结构和算法理解很简单,深感算法导论的介绍更是精辟而无累赘。

例如:1. 直接切入二叉搜索树,而不是从树开始介绍各种繁琐的表示方式,最后的重点结果还是二叉搜索和几种平衡树,算法导论介绍知识的时候数学性虽强,但应用性也十足,它的    应用性不在于给你代码,而在于给你应用的场景,告诉你各种结构的优劣和代价,这才是学习数据结构和算法应该掌握的精华,而不在一些教材上展示的可以称之为垃圾的代    码上,实际上,关于数据结构的实现代码,工业级的STL源码可以给你最高屋建瓴的精华。

     2. 遍历方法仅重点分析中序遍历,为何?因为按照二叉搜索树的性质其中序即使orderd,其它序也有作用,比如某一道习题提到的位二叉树,其pre-order才是orderd,但           比上来让你实现几个遍历,滚瓜烂熟之后也不知在何处用(只会用来display数据除外);

   3. 不止一次在网上看到人们评价算导的数据结构部分过于简单,又跑回去啃严版教材,实乃买椟还珠,为何他们觉得简单,我简单分析一下,认为他们一不做或者不思考习题,    对于二叉搜索树而言,最难的(其实也不难)地方在于迭代遍历(pre,in,post),算法导论的习题中不仅有,而且还提出一种使用stack模拟,另一种不借助stack,要求    读者独立完成。倘使不做,自然无法得其中奥义,只能回去啃别人代码,一读便懂,回头即忘。

  所以就我个人阅读感受而言,算法导论一书造诣颇高,比大多数相关书籍不知道高明多少,十分推荐。

我在以前只学习数据结构的时候就已经用模板实现了二叉搜索树,但是看完算法导论再加之一些STL源码的阅读体验,感觉之前的代码臭不可闻,接口错乱,实现冗杂,实乃错误之典范,究其根本还在于理解了但是不明白用来干嘛。当然,我偶然搜索了一下,发现大多数学习者在互相抄袭,完全相同的代码和用词出现在了好多的博文上,而其抄袭的代码本身也不是什么优秀之作。既然是学习,不管好与坏,自己做得才有进步,复制粘贴就。。。

所以最近得空重新实现一下,虽然还是有诸多不满之处,但比之前要好很多了。

下面在代码中分析一下:(设置为虚函数的函数是要做red-black tree中重新给出实现的操作)

算法的部分不难,理解之后还有很多设计上的问题,即接口和实现的分离、node类和tree类之间的耦合和内聚等等。

自己从头撸一个,还是有一些好处的。

  1 #include<iostream>
  2 using namespace std;
  3
  4 //结点类
  5 template<typename T>
  6 class TreeNode
  7 {
  8     //声明为二叉搜索树的友元类
  9     template<class T> friend class BinarySearchTree; 
 10     //方便<<操作符访问结点成员
 11     template<typename T> friend ostream& operator<<(ostream &os, BinarySearchTree<T>&BST);
 12
 13 private:
 14     T value;
 15     TreeNode<T>* lchild;
 16     TreeNode<T>* rchild;
 17     TreeNode<T>* parent;//带parent结点要方便的多
 18
 19     TreeNode<T>* _increase();//自增,即中序下的后继
 20     TreeNode<T>* _decrease();//自减,即中序下的前驱
 21 public:
 22     //三个构造函数
 23     TreeNode() :value(0), lchild(NULL), rchild(NULL),parent(NULL){}
 24     TreeNode(T v) :value(v), lchild(NULL), rchild(NULL),parent(NULL){}
 25     TreeNode(TreeNode<T> &node) :value(node.value), lchild(node.lchild), rchild(node.rchild),parent(node.parent){}
 26     virtual ~TreeNode(){} //析构函数设置为虚函数
 27     void _test_display()  //此函数只是测试使用,应该删去
 28     {
 29         cout << "value: " << this->value<<"     ";
 30         if (this->lchild!=NULL)
 31         cout <<"lchild: "<< this->lchild->value<<"  ";
 32         else  cout << "lchild: NULL"<<"  ";
 33         if (this->rchild != NULL)
 34             cout << "rchild: " << this->rchild->value << "  ";
 35         else  cout << "rchild: NULL" << "  ";
 36         if (this->parent != NULL)
 37             cout << "parent: " << this->parent->value << "  ";
 38         else  cout << "parent: NULL" << "  ";
 39         cout << endl;
 40     }
 41
 42 };
 43
 44 //二叉搜索树类
 45 template<typename T>
 46 class BinarySearchTree
 47 {
 48
 49 private:
 50     TreeNode<T> *root; //根节点
 51     int size;    //结点数量
 52
 53     TreeNode<T>* _copy(TreeNode<T> *node,TreeNode<T>* q); //私有函数,node表示复制以node为根节点的树,参数q实际上指向node的父节点,是实现的小技巧
 54     TreeNode<T>* _mininum(TreeNode<T>* node);             //私有函数,找到以node为根节点的树中的最小结点
 55     TreeNode<T>* _maxinum(TreeNode<T>* node);
 56
 57     virtual TreeNode<T>* _insert(T& value,TreeNode<T>* node);//私有函数,用于实现Insert操作
 58     virtual void _delete(TreeNode<T>* _delete_node,TreeNode<T>* node);//私有函数,用于实现Delete操作
 59     TreeNode<T>* _search(T& value, TreeNode<T>* node);               //私有函数,用于实现Search操作
 60     virtual void _init(T* array,int length);                //通过数组初始化二叉搜索树
 61     virtual void _clear(TreeNode<T>* node);                   //清空node为根节点的树
 62
 63
 64
 65 public:
 66     //构造和析构函数
 67     BinarySearchTree() :root(NULL), size(0){}
 68     BinarySearchTree(T* array, int length)    { _init(array, length); }
 69     BinarySearchTree(BinarySearchTree<T> &tree){ root = _copy(tree.root, NULL); size = tree.size; }
 70     virtual ~BinarySearchTree() { _clear(root); size = 0; }
 71     //赋值操作符的重载
 72     virtual BinarySearchTree<T>& operator=(BinarySearchTree<T> &tree){ _clear(root); root = _copy(tree.root, NULL);  size = tree.size; return *this; }
 73     //判断树是否为空
 74     bool isEmpty() { return size == 0; }
 75     //返回树中结点个数
 76     int Size()   { return size; }
 77     //基本操作,Insert、Delete、Search
 78     virtual TreeNode<T>*  Insert(T& value){ return _insert(value, root); }
 79     virtual void Delete(TreeNode<T>* node){ return _delete(node, root);  }
 80     TreeNode<T>* Search(T& value){ return _search(value, root); }
 81
 82     //返回树中value最大和最小的结点的value
 83     T& mininum(){ return _mininum(root)->value; }
 84     T& maxinum(){ return _maxinum(root)->value; }
 85     //返回某个节点的parent
 86     TreeNode<T>* parent(TreeNode<T> *node){ return node->parent; }
 87     //<<操作符必须设置为友元,不可以是成员
 88     template<typename T> friend ostream& operator<<(ostream &os, BinarySearchTree<T>&BST);
 89
 90     //一个测试函数
 91     void __test(){ cout << "测试_ decrease" << --(this->root->rchild->lchild->lchild)->value << endl; };
 92
 93 };
 94
 95 template<typename T>
 96 TreeNode<T>* BinarySearchTree<T>::_copy(TreeNode<T>* node,TreeNode<T>* q)
 97 {
 98     //这里q保存node的父节点,调用时初始化为NULL(root的parent为NULL)
 99
100     if (node == NULL)
101         return NULL;
102
103     TreeNode<T>* p = new TreeNode<T>(node->value);
104     p->parent = q;
105     p->lchild = _copy(node->lchild,p);//递归复制
106     p->rchild = _copy(node->rchild,p);
107     return p;
108 }
109
110
111 template<typename T>
112 TreeNode<T>* BinarySearchTree<T>::_mininum(TreeNode<T>* node)//最左端结点为最小
113 {
114     TreeNode<T> * p = node;
115     TreeNode<T>* q=NULL;
116     while (p != NULL)
117     {
118         q = p;
119         p = p->lchild;
120     }
121     return q;
122 }
123 template<typename T>
124 TreeNode<T>* BinarySearchTree<T>::_maxinum(TreeNode<T>* node)//最右端结点为最大
125 {
126     TreeNode<T>* p = node;
127     TreeNode<T>* q = NULL;
128     while(p != NULL)
129     {
130          q= p;
131         p = p->rchild;
132     }
133     return q;
134 }
135 template<typename T>
136 TreeNode<T>* TreeNode<T>::_increase()
137 {
138     if (this == NULL)
139         return NULL;
140     else
141     {
142         if (this->rchild != NULL)         //当前结点如果有右孩子,则后继为右子树中最小的结点
143         {
144             TreeNode<T> * p = this->rchild;
145             TreeNode<T>* q=p;
146             while (p != NULL)
147             {
148                 q= p;
149                 p = p->lchild;
150             }
151
152             return q;
153         }
154         else                                        //否则,则向上回溯,直到第一次出现 q 是 p 的左孩子结点为止
155         {
156             TreeNode<T> *q = this;
157             TreeNode<T> *p = this->parent;
158             //cout<<"parent:" << p->value << endl;
159             //cout <<"cur:   "<< q->value << endl;
160             while(q != p->lchild)
161             {
162                 q = p;
163                 p = p->parent;
164                 if (p == NULL)
165                     break;
166             }
167             //cout << "parent: " << p->value << endl;
168
169             return p;
170         }
171     }
172
173 }
174 template<typename T>
175 TreeNode<T>* TreeNode<T>::_decrease()
176 {
177     if (this == NULL)
178         return NULL;
179     else
180     {
181         if (this->lchild != NULL)                 //当前结点如果有左孩子,则后继为右子树中最大的结点
182         {
183             TreeNode<T> *p = this->lchild;
184             TreeNode<T> *q = p;
185             while (p != NULL)
186             {
187                 q = p;
188                 p = p->rchild;
189             }
190             return q;
191         }
192         else                                        //否则,则向上回溯,直到第一次出现 q 是 p 的右孩子结点为止
193         {
194             TreeNode<T> *q = this;
195             TreeNode<T> *p = this->parent;
196             while (q != p->rchild)
197             {
198                 q = p;
199                 p = p->parent;
200                 if (p == NULL)
201                     break;
202             }
203             return p;
204         }
205     }
206
207 }
208
209 template<typename T>
210 TreeNode<T>*  BinarySearchTree<T>::_insert(T& value, TreeNode<T>* node)  //insert操作的返回值为指向插入结点的指针
211 {
212     TreeNode<T> *p = new TreeNode<T>(value);
213     TreeNode<T> *parent_node = NULL;
214
215     while (node != NULL)
216     {
217         if (p->value < node->value)
218         {
219             parent_node = node;
220             node = node->lchild;
221         }
222         else
223         {
224             parent_node = node;
225             node = node->rchild;
226         }
227     }
228     // 找到待插入结点parent_node
229     if (parent_node != NULL)
230     {
231         p->parent = parent_node;
232         if (p->value < parent_node->value)
233         {
234             parent_node->lchild = p;
235         }
236         else
237         {
238             parent_node->rchild = p;
239         }
240     }
241     else   //当前树为空
242     {
243         root=p;
244     }
245     return p;
246
247 }
248 template<typename T>
249 void BinarySearchTree<T>::_delete(TreeNode<T>* _delete_node, TreeNode<T>* node)
250 {
251     TreeNode<T> *y, *x;
252     if (_delete_node->lchild == NULL || _delete_node->rchild == NULL)  //如果待删除结点有一个孩子或者没有孩子,那么要被移除的结点就是它自己
253          y = _delete_node;
254     else y = _delete_node->_increase();        //如果有两个结点,那么要移除的结点就是它的后继(然后把它的后继的value赋值给它)
255
256     if (y->lchild != NULL)
257         x = y->lchild;    //如果y的左孩子不空的话,赋值给x
258     else x = y->rchild;        //否则,无论是右孩子空不空,都赋值给x
259
260     TreeNode<T> *parent_of_y = parent(y);
261
262     if (y != _delete_node)
263     {
264         _delete_node->value = y->value;
265         x->parent = parent_of_y;
266         if (y == parent_of_y->lchild)
267             parent_of_y->lchild = x;
268         else
269             parent_of_y->rchild = x;
270         delete y;
271     }
272     else
273     {
274         x->parent = parent_of_y;
275         if (parent_of_y == NULL)
276         {
277             node = x;
278
279             delete y;
280         }
281         else
282         {
283             if (parent_of_y->lchild == y)
284                 parent_of_y->lchild = x;
285             else
286                 parent_of_y->rchild = x;
287             delete y;
288         }
289     }
290
291 }
292 template<typename T>
293 TreeNode<T>* BinarySearchTree<T>::_search(T& value, TreeNode<T>* node)  //search是其最擅长的操作,返回值为找到结点的指针
294 {
295     TreeNode<T>* p = node;
296     while (p != NULL)
297     {
298         if (value < p->value)
299             p = p->lchild;
300         else if (value > p->value)
301             p = p->rchild;
302         else return p;
303     }
304     return NULL;
305 }
306 template<typename T>
307 void BinarySearchTree<T>::_init(T* array,int length)     //反复调用insert操作来初始化,并且增大size
308 {
309
310     for (int i = 0; i < length; ++i)
311     {
312         _insert(array[i], root);
313         ++size;
314     }
315 }
316
317 template<typename T>
318 void BinarySearchTree<T>::_clear(TreeNode<T>* node)    //递归调用来删除
319 {
320     if (node == NULL)
321         return;
322
323     TreeNode<T>* p = node->lchild;
324     TreeNode<T>* q = node->rchild;
325     delete node;
326     _clear(p);
327     _clear(q);
328 }
329
330 template<typename T>
331 ostream& operator<<(ostream &os, BinarySearchTree<T>& BST)    //这里其实是一个迭代版(不用辅助stack)的方法
332 {
333     TreeNode<T>* node = BST.root;
334     while (true)
335     {
336         if (node->lchild != NULL)        //一直访问到当前最左边结点
337             node = node->lchild;
338         else
339         {
340             os << node->value << "  ";  //输出当前结点的value
341             while (node->rchild == NULL)          //如果无右孩子,则访问其后继,注意这里是循环
342             {
343
344                 node=node->_increase();
345
346                 if (node != NULL)
347                     os << node->value << "  ";
348                 else break;
349             }
350             if (node !=NULL)                //如果有右孩子,访问其右孩子(这里是一个尾递归优化而来的迭代,容易理解)
351             {
352                 node = node->rchild;
353             }
354             else break;
355
356         }
357
358     }
359     return os;
360 }
361 int main()
362 {
363     const int length = 9;
364     int array[length] = { 13,9,17,5,12,15,18,2,19};
365     //检测_init    _insert    operator<<    _increase
366     BinarySearchTree<int> BST(array, length);
367     cout <<"BST: "<< BST << endl;
368     int v = 14;
369     BST.Insert(v);
370     cout<<"BST insert one node with value 14: " << BST << endl;
371
372
373     //检测_copy,okay
374     BinarySearchTree<int> Bst(BST);
375     cout << Bst << endl;
376
377     //检测operator=,okay
378     BinarySearchTree<int> bst,bsT;
379     bsT= bst = Bst;
380     cout <<"!"<< bst<<endl;
381     cout <<"!"<< bsT << endl;
382
383     //检测_mininum  _maxinum,okay
384     cout << "maxinum" << BST.maxinum()<<endl;
385     cout << "mininum" << BST.mininum()<<endl;
386
387     //检测 _decrease,okay
388     BST.__test();
389
390     //检测_search,okay
391     TreeNode<int> *p=BST.Search(array[0]);
392     p->_test_display();
393     p = BST.Search(array[7]);
394     p->_test_display();
395     p = BST.Search(array[8]);
396     p->_test_display();
397
398
399     //检测_delete,okay
400     p = BST.Search(array[2]);
401     BST.Delete(p);
402     cout << "delete the node with value 17"<<endl;
403     cout << BST << endl;
404
405     //测试size
406     cout <<"BST size: "<< BST.Size() << endl;
407     cout <<"bsT size: "<< bsT.Size() << endl;
408     system("pause");
409
410 }

BinarySearchTree示例——C++模板实现

时间: 2024-10-13 09:33:14

BinarySearchTree示例——C++模板实现的相关文章

【转】 C++模板详解

C++模板 模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数.返回值取得任意类型. 模板是一种对类型进行参数化的工具: 通常有两种形式:函数模板和类模板: 函数模板针对仅参数类型不同的函数: 类模板针对仅数据成员和成员函数类型不同的类. 使用模板的目的就是能够让程序员编写与类型无关的代码.比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型

C++ 模板详解(二)(转)

四.类模板的默认模板类型形参 1.可以为类模板的类型形参提供默认值,但不能为函数模板的类型形参提供默认值.函数模板和类模板都可以为模板的非类型形参提供默认值. 2.类模板的类型形参默认值形式为:template<class T1, class T2=int> class A{};为第二个模板类型形参T2提供int型的默认值. 3.类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从第一个形参设定了默认值之后的所有模板形参都要设定默认值,比如template<class T1=

Django中模板总结[模板语言,模板继承,CSRF攻击,反向解析]

模板 概述 作为Web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快更方便的完成页面开发,再通过在视图中渲染模板,将生成最终的html字符串返回给客户端浏览器.模版致力于表达外观,而不是程序逻辑.模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用. 创建模板文件夹 在项目的文件里面定义一个templates文件夹 在创建的文件夹里面再创建一个文件夹为每个应用的html文件夹 起名为和应用名同名的文

Razor - 模板引擎 / 代码生成 - RazorEngine

目录 Brief Authors Official Website RazorEngine 的原理 - 官方解释 安装记录 Supported Syntax (默认实现支持的语法) 测试记录 - can't cleanup temp files 测试记录 - Quick Start 测试记录 - Configuration 测试记录 - 对比 3 种 Type 的 model 的语法 测试记录 - 扩展模板语法 测试记录 - Layout 测试记录 - Partial (@Include())

C++ 模板详解(二)

C++模板 四.类模板的默认模板类型形参 1.可以为类模板的类型形参提供默认值,但不能为函数模板的类型形参提供默认值.函数模板和类模板都可以为模板的非类型形参提供默认值. 2.类模板的类型形参默认值形式为:template<class T1, class T2=int> class A{};为第二个模板类型形参T2提供int型的默认值. 3.类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从第一个形参设定了默认值之后的所有模板形参都要设定默认值,比如template<cla

MyEclipse使用教程:按需点播的MyEclipse示例

1. 什么是示例点播? 当学习或评估技术.架构或设计技术时,软件开发人员会告诉你,在开发实践中没有什么能比得上工作示例对于一个成功采用以上元素的项目的价值.为了便于开发人员的快速学习,Genuitec提供示例点播,向软件开发者提供了可以随时运行示例应用程序的目录,这些应用程序演示了如何快速而成功地使用流行的编程技术和框架,如Ajax框架.JSP.JSF.Struts.Spring.Hibernate.JPA和EJB3等等.每个示例点播应用程序都被设计在MyEclipse的零配置服务器沙箱中运行,

初识Angularjs示例

初识Angularjs使用了官方的示例代码 模板: <html ng-app>    <head>       <script src="/angular/angular.js"></script>       <script src="/angular/controllerjs/controllers.js"></script>   </head>   <body ng-co

Django之模板语言

Django之模板语言 一 模板的执行 模板的创建过程,对于模板,其实就是读取模板(其中嵌套着模板的标签),然后将Model中获取的数据插入到模板中,最后将信息返回给用户 def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse

C++解析(26):函数模板与类模板

0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1.函数模板 1.1 函数模板与泛型编程 C++中有几种交换变量的方法? 交换变量的方法--定义宏代码块 vs 定义函数: 定义宏代码块 优点:代码复用,适合所有的类型 缺点:编译器不知道宏的存在,缺少类型检查 定义函数 优点:真正的函数调用,编译器对类型进行检查 缺点:根据类型重复定义函数,无法代码复