二叉搜索树的简单实现(Binary Search Tree)
2015-09-18 19:49
435 查看
一、二叉搜索树的概念
二叉搜索树,又称二叉排序树,二叉查找树,他能够高效的完成数据的插入,查询,删除操作,是一种高效的数据结构。
如下图就是一个构建好的二叉搜索树。
特点:
所有的结点,都满足左子树上的所有节点都比自己小,而右子树上的所有节点都比自己大。
二、二叉搜索树的结构
查找:
根据上述特性,我们可以很轻松的写出查找某一个数(X)是否存在的算法。
如果当前结点比X大,就去查找左儿子
如果当前结点比X小,就去查找右儿子
直到找到节点,或者儿子为空。
例如我们查找上图的二叉搜索树中是否含有数字10,
根结点的数值为7 比10小,所以往右走,走到结点的数值为15,比10大,再往左走,找到10。
插入:
如何插入数值(X)呢?我们可以按照刚才查找数值的方法去试图找这个数值(X)的结点,就可以知道X位置了,之后在那个位置插入新的结点即可。
如图所示插入元素6。
删除:
最后是删除数值。
例如我们要删除15,如果直接删除15,那么15的子节点就悬空了,我们要找一个子节点来代替15的位置。
一般来说有三种情况。
① 需要删除的结点没有左儿子,那么就把右儿子提上去。
② 需要删除的结点的左儿子没有右儿子,那么就把左儿子提上去。
③ 以上两种条件都不满足,就把左儿子子孙结点的最大结点提到要删除的结点上。
如图所示删除节点 15. (对应了第三种情况)
三、二叉搜索树的复杂度
可以看出,不论哪一种操作,所花的时间都和树的高度成正比,因此如果共有n个元素,平均需要 O(log n)。
注意到,平均两个字,二叉搜索树也是有bug的,当出现树退化的时候,二叉搜索树的效率可能达到上限 O(n),为了防止这个事情的发生就要实现平衡二叉树了,这里先留个坑,日后补上。
四、二叉搜索树的实现
通过上面二叉搜索树原理的介绍,我们就来简单的实现一下这个数据结构增删查的功能吧。
#include <iostream> #include <cstdio> using namespace std; struct node{ int val; node *lch,*rch; node(){ val = 0; lch = rch = NULL; } }; node* insert(node* p,int x){ //插入操作 if(p == NULL){ node* q = new node; q->val = x; q->lch = q->rch = NULL; return q; } else{ if(x < p->val) p->lch = insert(p->lch,x); else p->rch = insert(p->rch,x); return p; } } bool find(node* p,int x){ if(p == NULL) return false; else if(x == p->val) return true; else if(x < p->val) return find(p->lch,x); else return find(p->rch,x); } //删除操作 //1.需要删除的结点没有左儿子,把右儿子提上去 //2.需要删除的结点的左儿子没有右儿子,把左儿子提上去 //3.以上两种情况都不满足,把左儿子子孙中最大的结点提到要删除的结点位置。 node* remove(node* p,int x){ if(p == NULL) return NULL; else if(x < p->val) p->lch = remove(p->lch,x); else if(x > p->val) p->rch = remove(p->rch,x); //将返回的值 赋值给 右儿子 else if(p->lch == NULL){ //1. node* q = p->rch; delete p; return q; } else if(p->lch->rch == NULL){ //2. node* q = p->lch; q->rch = p->rch; delete p; return q; } else { node* q; for(q = p->lch;q->rch->rch != NULL;q = q->rch); node *r = q->rch; //将找到的”左儿子子孙中最大的结点“ 赋值给r q->rch = r->lch; //将最大结点原先的左子节点赋值给 上一级结点右结点 //经过这两步,最大结点已经提出来了 r->lch = p->lch; //最大结点取代被删除结点的位置 r->rch = p->rch; delete p; return r; } return p; } void print(node* tmp){ if(tmp == NULL){ return ; } else{ print(tmp->lch); //递归向左子树找到最小的值 printf("%d\n",tmp->val); //输出 (中序遍历) print(tmp->rch); //再找右子树 } } int main(){ node* tmp; tmp = NULL; //不要忘记清空 tmp = insert(tmp,7); //测试数据 tmp = insert(tmp,2); tmp = insert(tmp,15); tmp = insert(tmp,10); tmp = insert(tmp,11); tmp = insert(tmp,17); tmp = insert(tmp,16); tmp = insert(tmp,19); tmp = insert(tmp,8); tmp = remove(tmp,15); print(tmp); int num = 15; if(find(tmp,num)) printf("Find %d !\n",num); else printf("Not find!\n"); num = 16; if(find(tmp,num)) printf("Find %d !\n",num); else printf("Not find!\n"); return 0; }
另一种实现方法
这里再贴一份网易云课堂上的构造方法,也是课本上比较普遍的构造方法 , 删除的时候与上述代码有所不同,应该更加容易理解
新增加了查找最小值,最大值的函数,方便在删除操作时应用。
#include<iostream> #include<cstdio> using namespace std; typedef int ElementType; struct node{ ElementType Data; node *Left,*Right; node(){ Left = Right = NULL; Data = 0; } }; typedef node* BinTree; /* BinTree Find(ElementType X,BinTree BST){ //递归方法, 尾递归,效率低 if(!BST) return NULL; if(X >BST->Data) return Find(X , BST->Right); else if(X < BST->Data) return Find(X , BST->Left); else //X == BST->Data; return BST ; } */ BinTree Find(ElementType X, BinTree BST){ //迭代版效率高, 可将尾递归 改为迭代函数 while(BST){ if(X >BST->Data) BST = BST->Right; else if(X <BST->Data) BST = BST->Left; else return BST; } return NULL; } BinTree FindMin(BinTree BST){ //迭代版本 while(BST->Left) BST = BST->Left; return BST; } BinTree FindMax(BinTree BST){ //递归版本 if(BST->Right) return FindMax(BST->Right); return BST; } BinTree Insert(ElementType X,BinTree BST){ if(!BST){ BST = new node; BST->Data = X; BST->Left = BST->Right = NULL; } else{ if(X < BST->Data) BST->Left = Insert(X , BST->Left); else //这样保证了重复节点的插入。即X == BST->Data; BST->Right = Insert(X , BST->Right); } return BST; } //删除操作 // 1、要删除的 节点 只有一个儿子 删除当前节点,将这个儿子提到当前位置; // 2、要删除的节点 没有儿子,直接删除即可; // 3、要删除的节点既有左儿子,又有右儿子。(这里有两种处理方法) // 1 。 找到右儿子后代中最小的那个儿子 ,覆盖当前位置, 删除那个儿子。 // 2 。 找到左儿子后代最大的那个儿子,覆盖当前位置, 删除那个儿子。 BinTree Delete(ElementType X , BinTree BST){ BinTree Tmp; if(!BST) printf("要删除的元素未找到\n"); else if(X < BST->Data) BST->Left = Delete(X , BST->Left); //递归的删除左子树 else if(X > BST->Data) BST->Right = Delete(X, BST->Right); else//找到要删除的节点 if(BST->Left && BST->Right){ //如果既有左儿子又有右儿子 Tmp = FindMin(BST -> Right); //这里我们找到右儿子中最小的儿子 BST->Data = Tmp->Data; //赋值给当前节点 BST->Right = Delete(BST->Data , BST->Right); //递归的删除那个右儿子 } else{ //被删除的节点的子节点只有一个,或者没有 Tmp = BST; if(!BST->Left) //如果左边为空 BST = BST->Right; //这里隐式的处理了左右儿子都为空的情况。 else if(!BST->Right) BST = BST->Left; delete Tmp; //删除当前节点 } return BST; } void Traverse(BinTree BST){ //中序遍历二叉搜索树 if(!BST) return ; else{ Traverse(BST->Left); printf("%d\n" , BST->Data); Traverse(BST->Right); } } int main(){ BinTree tree = NULL; tree = Insert(5,tree); tree = Insert(3,tree); tree = Insert(2,tree); tree = Insert(5,tree); tree = Insert(7,tree); tree = Insert(67,tree); Traverse(tree); cout<<"------------"<<endl; tree = Delete(67,tree); tree = Delete(9,tree); BinTree tmp = Find(2,tree); printf("查找 %d\n",tmp->Data); Traverse(tree); return 0; }
相关文章推荐
- Java基础---String类和基本数据类型包装类
- uva 291 The house of santa claus DFS
- ios开发必备10款第三方类库
- 测试
- Android 自定义View实现SlideSwitchView
- HDOJ-4498 Function Curve(自适应Simpson积分)
- android:windowSoftInputMode属性详解
- Android网络访问Post请求的两种写法
- 数组:最多连续数的子集
- ruby应用里添加zepto.min.js但是无法使用里面的方法
- UVA 10689 Yet another Number Sequence
- 白盒测试
- hdu 1181 变形课(floyed算法)
- hdu 5381 莫队...
- android init.rc文件语法详解
- javaScript对象
- hadoop、hbase、hive、zookeeper版本对应关系
- ruby应用里添加zepto.min.js但是无法使用里面的方法
- 电梯算法
- 第 八 天 :我 的 计 划