一、 实验目的
理解二叉排序树与平衡二叉树的概念,掌握二叉排序树的构造方法,实现二叉排序树的插入、删除与查找算法。
二、 实验内容
通过编写程序,实现二叉排序树的查找算法。
具体步骤如下:
- 创建二叉排序树的查找函数、插入函数和删除函数;
- 在主函数中,创建二叉排序树,输入要查找的关键字,调用相应的子函数,实现二叉排序树的相关算法。
三、 实验工具
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("
【请依次输入二叉排序树元素】
");
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("
【待删除的元素不存在!】
");
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("
************菜单***********
");
printf("1.创建二叉排序树;
");
printf("
2.二叉排序树的查找;
");
printf("
3.二叉排序树的插入;
");
printf("
4.二叉排序树的删除;
");
printf("
0.退出;
");
printf("***************************
");
printf("【请输入你的选择】
>>>");
}
//主函数
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("
【二叉排序树创建成功!】
");
printf("当前有序二叉树中序遍历结果为:");
InOrderTraverse(T);
break;
}
case 2:{
printf("
【请输入待查找元素】
");
getchar();
scanf("%c",&key);
result = SearchBST(T,key);
if(result)
printf("【查找成功!】
该元素%c地址为:%d
",result->data,result);
else
printf("【查找失败!】
该元素%c可能不存在
",key);
break;
}
case 3:{
printf("
【请输入待插入元素】
");
getchar();
scanf("%c",&e.key);
if(InsertBST(T,e))
printf("
【%c插入成功!】",e.key);
else
printf("
【%c插入失败!可能待插入元素已存在!
】",e.key);
printf("当前有序二叉树中序遍历结果为:");
InOrderTraverse(T);
break;
}
case 4:{
printf("
【请输入待删除元素】
");
getchar();
scanf("%c",&key);
DeleteBST(T,key);
printf("当前有序二叉树中序遍历结果为:");
InOrderTraverse(T);
break;
}
case 0:exit(0);
}
}
return 0;
}
五、 实验结果
- 创建二叉排序树
- 二叉排序树查找
- 二叉排序树插入
- 二叉排序树删除
- 退出
六、 总结与思考
1. 二叉排序树是递归定义的,得到一个重要性质:中序遍历二叉排序树,可以得到结点值递增的有序序列。
2. 当先后插入二叉排序树有序时,生成单支树,单支树的查找性能较差。
3. 二叉排序树的查找与折半查找类似,但就维护表的有序性而言,二叉排序树更加有效,,对于需要经常进行插入、删除和查找运算操作的表,采用二叉排序树较好。
4. 最好的情况下,平均查找长度与折半查找判定树相似,与 log 2n 成正比。