一、 实验目的

理解二叉排序树与平衡二叉树的概念,掌握二叉排序树的构造方法,实现二叉排序树的插入、删除与查找算法。

二、 实验内容

通过编写程序,实现二叉排序树的查找算法。
具体步骤如下:

  1. 创建二叉排序树的查找函数、插入函数和删除函数;
  2. 在主函数中,创建二叉排序树,输入要查找的关键字,调用相应的子函数,实现二叉排序树的相关算法。

三、 实验工具

Dev - C++

四、实验代码

//Authors:xiaobei

//算法1 二叉排序树的递归查找
//算法2 二叉排序树的插入
//算法3 二叉排序树的创建
//算法4 二叉排序树的删除
#include<stdio.h>
#include<stdlib.h>
#define ENDFLAG '#'
#define OK 1
#define OERROR 0
//二叉树存储表示
typedef struct ElemType{ 
 char key;
}ElemType;
typedef struct BSTNode{
 ElemType data; //结点数据域
 BSTNode *lchild,*rchild; //左右孩子指针
}BSTNode,*BSTree;
//函数二叉排序树的递归查找
BSTree SearchBST(BSTree T,char key){
  //在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素
  //若查找成功,则返回指向该数据元素结点的指针,否则返回空指针
  if((!T)|| key==T->data.key) return T;                     //查找结束
  else if (key<T->data.key)  return SearchBST(T->lchild,key); //在左子树中继续查找
  else return SearchBST(T->rchild,key);             //在右子树中继续查找
}
//函数二叉排序树的插入
int InsertBST(BSTree &T,ElemType e){
  //当二叉排序树T中不存在关键字等于e.key的数据元素时,则插入该元素
 BSTree S;
 if(!T){                     //找到插入位置,递归结束
   S = (BSTNode*)malloc(sizeof(BSTNode));            //生成新结点*S
         S->data = e;                    //新结点*S的数据域置为e   
         S->lchild = S->rchild = NULL;  //新结点*S作为叶子结点
         T = S;                 //把新结点*S链接到已找到的插入位置
         return 1;
 }
 else if(e.key< T->data.key)
  InsertBST(T->lchild,e);    //将*S插入左子树
 else if(e.key> T->data.key) 
  InsertBST(T->rchild,e);    //将*S插入右子树
 else
  return 0;
}
//函数二叉排序树的创建
int CreateBST(BSTree &T){
  //依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
  T=NULL;
  ElemType e;
  printf("\n【请依次输入二叉排序树元素】\n");
  scanf("%c",&e.key);
  while(e.key!=ENDFLAG){        //ENDFLAG为自定义常量,作为输入结束标志
    InsertBST(T, e);              //将此结点插入二叉排序树T中
    scanf("%c",&e.key);
  }//while
  return OK;
}
//函数二叉排序树的删除
void DeleteBST(BSTree &T,char key){
  //从二叉排序树T中删除关键字等于key的结点
   BSTree p = T;
   BSTree f = NULL;                       //初始化
   BSTree q;
   BSTree s;
 /*------------下面的while循环从根开始查找关键字等于key的结点*p-------------*/
   while(p){                  
    if (p->data.key == key)
     break;               //找到关键字等于key的结点*p,结束循环
    f = p;                                //*f为*p的双亲结点
    if(p->data.key> key)
     p=p->lchild;          //在*p的左子树中继续查找
    else
     p=p->rchild;                      //在*p的右子树中继续查找
   }//while
  if(!p){
   printf("\n【待删除的元素不存在!】\n");
   return;                          //找不到被删结点则返回
  }
/*―考虑三种情况实现p所指子树内部的处理:*p左右子树均不空、无右子树、无左子树―*/
 if ((p->lchild) && (p->rchild)){       //被删结点*p左右子树均不空
   q = p;
   s = p->lchild;
   while (s->rchild){                  //在*p的左子树中继续查找其前驱结点,即最右下结点
    q = s;
    s = s->rchild;
   }                 //向右到尽头
   p->data = s->data;                 //s指向被删结点的“前驱”
   if(q!=p){
    q->rchild = s->lchild;       //重接*q的右子树
   }
   else 
    q->lchild = s->lchild;          //重接*q的左子树
   delete s;
   }//if
 else{
  if(!p->rchild) {                  //被删结点*p无右子树,只需重接其左子树
     q = p; p = p->lchild; 
    }//else if
  else if(!p->lchild) {                //被删结点*p无左子树,只需重接其右子树
    q = p; p = p->rchild;
    }//else if
  /*――――――――――将p所指的子树挂接到其双亲结点*f相应的位置――――――――*/
    if(!f) T=p;                        //被删结点为根结点
    else if (q==f->lchild) f->lchild = p; //挂接到*f的左子树位置
    else f->rchild = p;                  //挂接到*f的右子树位置
    delete q;
 }
}
//二叉树中序遍历
void InOrderTraverse(BSTree &T){
 if(T)
 {
 InOrderTraverse(T->lchild);
 printf("%c",T->data.key);
 InOrderTraverse(T->rchild);
 }
}
//函数打印菜单
void Menu(){
 printf("\n************菜单***********\n");
 printf("1.创建二叉排序树;\n");
 printf("\n2.二叉排序树的查找;\n");
 printf("\n3.二叉排序树的插入;\n");
 printf("\n4.二叉排序树的删除;\n");
 printf("\n0.退出;\n");
 printf("***************************\n");
 printf("【请输入你的选择】\n>>>");
}
//主函数 
int main(){
 BSTree T,result;
 ElemType e;
 int user;
 char key;
 while(true){
  Menu();
  scanf("%d",&user);
  switch(user){
  case 1:{
   if(CreateBST(T))
    printf("\n【二叉排序树创建成功!】\n");
   printf("当前有序二叉树中序遍历结果为:");
   InOrderTraverse(T);
   break;
      }
  case 2:{
   printf("\n【请输入待查找元素】\n");
   getchar();
   scanf("%c",&key);
   result = SearchBST(T,key);
   if(result)
    printf("【查找成功!】\n该元素%c地址为:%d\n",result->data,result);
   else
    printf("【查找失败!】\n该元素%c可能不存在\n",key);
   break;
      }
  case 3:{
   printf("\n【请输入待插入元素】\n");
   getchar();
   scanf("%c",&e.key);
   if(InsertBST(T,e))
    printf("\n【%c插入成功!】",e.key);
   else
    printf("\n【%c插入失败!可能待插入元素已存在!\n】",e.key);
   printf("当前有序二叉树中序遍历结果为:");
   InOrderTraverse(T);
   break;
      }
  case 4:{
   printf("\n【请输入待删除元素】\n");
   getchar();
   scanf("%c",&key);
   DeleteBST(T,key);
   printf("当前有序二叉树中序遍历结果为:");
   InOrderTraverse(T);
   break;
      }
  case 0:exit(0);
  }
 }
 return 0;
}

五、 实验结果

  1. 创建二叉排序树
    1
  2. 二叉排序树查找
    2
  3. 二叉排序树插入
    3
  4. 二叉排序树删除
    4
  5. 退出
    5

六、 总结与思考

1. 二叉排序树是递归定义的,得到一个重要性质:中序遍历二叉排序树,可以得到结点值递增的有序序列。

2. 当先后插入二叉排序树有序时,生成单支树,单支树的查找性能较差。

3. 二叉排序树的查找与折半查找类似,但就维护表的有序性而言,二叉排序树更加有效,,对于需要经常进行插入、删除和查找运算操作的表,采用二叉排序树较好。

4. 最好的情况下,平均查找长度与折半查找判定树相似,与 log 2n 成正比。