您的位置:首页 > 其它

二叉搜索树的简单实现(Binary Search Tree)

2015-09-18 19:49 381 查看



一、二叉搜索树的概念


二叉搜索树,又称二叉排序树,二叉查找树,他能够高效的完成数据的插入,查询,删除操作,是一种高效的数据结构。



如下图就是一个构建好的二叉搜索树。






特点:



所有的结点,都满足左子树上的所有节点都比自己小,而右子树上的所有节点都比自己大。


二、二叉搜索树的结构


查找:



根据上述特性,我们可以很轻松的写出查找某一个数(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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: