您的位置:首页 > 编程语言 > C语言/C++

二叉排序(查找,搜索)树详解

2017-10-22 16:24 190 查看
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
一,储存方式
按二叉树的存储方式,定义一个结构体,包含节点的值,指向左右子树的指针;
typedef struct treeNode{
int x;
struct treeNode *lchild,*rchild;
}BSTree;


二,初始化函数
BSTree *BSTreeInsert(BSTree *Root,int n){
BSTree *q,*p=Root;
while(p){
q=p;
if(p->x>n)p=p->lchild;
else p=p->rchild;
}
p=(BSTree*)malloc(sizeof(BSTree));
p->x=n;
p->lchild=p->rchild=NULL;
if(Root==NULL)Root=p;
else if(q->x>n)q->lchild=p;
else q->rchild=p;
return Root;
}
BSTree *CreatBSTree(int a[],int n){
BSTree *Root=NULL;
int i=0;
while(i<n){
Root=BSTreeInsert(Root,a[i]);
i++;
}
return Root;
}
int main(){
int a[5]={23,1,45,8,12};
BSTree* Root=CreatBSTree(a,5);
return 0;
}
采用一个个插入的方法构建;
先通过while循环找到要插入节点p的前驱q的位置,并判断p应插入q的左还是右子结点;
如果是空树即Root==NULL,则Root指向p;

三,排序
因为二叉排序树的每一个结点都大于其左子树,小于其右子树,那么对其进行中序遍历即可得到递增序列
void display(BSTree* Root){
if(Root){
display(Root->lchild);
cout<<(Root->x)<<" ";
display(Root->rchild);
}
}


四,查找
简单,无需解释,直接上代码
BSTree* BSTreeSerch(BSTree* Root,int k){
if(Root==NULL)return NULL;
else{
while(Root!=NULL){
if(k==Root->x)return Root;
else if(k>Root->x)Root=Root->rchild;
else Root=Root->lchild;
}
}
return NULL;
}


五,删除结点(难点)

删除结点要保证删除后的树仍然为一颗二叉查找树,即每一个结点都大于其左子树,小于其右子树;
理论上分为三种情况:
(1)要删除的结点为叶子结点(无左右子数);

         直接删除;
(2)要删除的结点有左子树或者有右子树;
         将要删除结点的左子树或者右子树变为要删除结点前驱(双亲)的左子树或者右子树,并删除结点;
(3)有左右子树;
         用左子树中最大的数取代要删除结点的值并删除最大数所在的结点
         或者用右子树中最小的数代替要删除结点的值并删除最下的数所在的结点;

实际实现:

(一)无左子树;
(1)是根节点:根节点直接指向其右子树;
(2)不是根节点:若要删除的结点为其前驱结点(双亲结点)的左子结点,其前驱结点(双亲结点)的左子节点指向要删除结点的左子节点,若要删除结点为其前驱结点(双亲结点)的右子结点,其前驱结点(双亲结点)的右子结点指向要删除结点的左子节点。

(二)有右子树;
用左子树中最大的数取代要删除结点的值并删除最大数所在的结点,因为每个结点都小于其右子结点,所以最大的数所在的结点无右子结点,而其左子树也是最大数所在结点的前驱结点(双亲结点)的右子树的一部分(都大于双亲结点的值)所以如果最大值所在的结点有左子树的话,直接把其左子树接到其前驱结点(双亲结点)的右子结点;
具体代码:
BSTree* BSTreeDelete(BSTree* Root,int n) {
BSTree *p=Root,*q,*s,*f=NULL;
while(p){
if(p->x==n)break;
f=p;//f为p的前驱
if(p->x>n)p=p->lchild;
else p=p->rchild;
}
if(p==NULL)return Root;//找不到就返回
if(p->lchild==NULL){  //无左子树
if(f==NULL)Root=p->rchild;//未进入while循环,p为根节点 (因为要用到f为p前驱,
else if(f->lchild==p)f->lchild=p->rchild;    //所以得判断是否为根节点)
else f->rchild=p->rchild;
free(p);
}else {   //有左子树
q=p;s=p->lchild;
while(s->rchild!=NULL){
q=s;
s=s->rchild;
}
if(q==p)p->lchild=s->lchild;//未进入while循环,s为p左子树,s无右子树.将s的左子树连到q上
else q->rchild=s->lchild;
p->x=s->x;
free(s);
}
return Root;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息