二叉排序树运算数据结构与算法课程设计报告 l.docx

上传人:b****6 文档编号:8446484 上传时间:2023-01-31 格式:DOCX 页数:29 大小:235.06KB
下载 相关 举报
二叉排序树运算数据结构与算法课程设计报告 l.docx_第1页
第1页 / 共29页
二叉排序树运算数据结构与算法课程设计报告 l.docx_第2页
第2页 / 共29页
二叉排序树运算数据结构与算法课程设计报告 l.docx_第3页
第3页 / 共29页
二叉排序树运算数据结构与算法课程设计报告 l.docx_第4页
第4页 / 共29页
二叉排序树运算数据结构与算法课程设计报告 l.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

二叉排序树运算数据结构与算法课程设计报告 l.docx

《二叉排序树运算数据结构与算法课程设计报告 l.docx》由会员分享,可在线阅读,更多相关《二叉排序树运算数据结构与算法课程设计报告 l.docx(29页珍藏版)》请在冰豆网上搜索。

二叉排序树运算数据结构与算法课程设计报告 l.docx

二叉排序树运算数据结构与算法课程设计报告l

合肥学院

计算机科学与技术系

 

课程设计报告

2009~2010学年第二学期

 

课程

数据结构与算法

课程设计名称

二叉排序树运算

学生姓名

顾成方

学号

0704011033

专业班级

08计科

(2)

指导教师

王昆仑张贯虹

2010年5月

题目:

(二叉排序树运算问题)设计程序完成如下要求:

对一组数据构造二叉排序树,并在二叉排序树中实现多种方式的查找。

基本任务:

⑴选择合适的储存结构构造二叉排序树;⑵对二叉排序树T作中序遍历,输出结果;⑶在二叉排序树中实现多种方式的查找,并给出二叉排序树中插入和删除的操作。

⑷尽量给出“顺序和链式”两种不同结构下的操作,并比较。

一、问题分析和任务定义

本次程序需要完成如下要求:

首先输入任一组数据,使之构造成二叉排序树,并对其作中序遍历,然后输出遍历后的数据序列;其次,该二叉排序树能实现对数据(即二叉排序树的结点)的查找、插入和删除等基本操作。

实现本程序需要解决以下几个问题:

1、如何构造二叉排序树。

2、如何通过中序遍历输出二叉排序树。

3、如何实现多种查找。

4、如何实现插入删除等操作。

二叉排序树的定义:

1其左子树非空,则左子树上所有结点的值均小于根结点的值。

2若其右子树非空,则右子树上所有结点的值大于根结点的值。

3其左右子树也分别为二叉排序树。

本问题的关键在于对于二叉排序树的构造。

根据上述二叉排序树二叉排序树的生成需要通过插入算法来实现:

输入(插入)的第一个数据即为根结点;继续插入,当插入的新结点的关键值小于根结点的值时就作为左孩子,当插入的新结点的关键值大于根结点的值时就作为右孩子;在左右子树中插入方法与整个二叉排序树相同。

当二叉排序树建立完成后,要插入新的数据时,要先判断已建立的二叉排序树序列中是否已有当前插入数据。

因此,插入算法还要包括对数据的查找判断过程。

本问题的难点在于二叉排序树的删除算法的实现。

删除前,首先要进行查找,判断给出的结点是否已存在于二叉排序树之中;在删除时,为了保证删除结点后的二叉树仍为二叉排序树,要考虑各种情况,选择正确的方法。

删除操作要分几种情况讨论,在后面有介绍。

二、概要设计和数据结构选择

用二叉链表作为二叉排序树的存储结构,其中key为结点关键值,*lchlid、*rchild分别为左右孩子指针。

该程序的结构如下图所示:

 

三、详细设计和编码

首先定义二叉排序树的数据类型如下:

typedefstructnode

{

intkey;//关键字项

structnode*lchild,*rchild;//左右孩子指针

}Bstnode;

然后按一定顺序来编写算法程序:

1、递归查找算法

具体思想如下:

(1)若二叉树为空,则查找失败。

(2)否则,将根结点的关键值与待查关键字进行比较,若相等,则查找成功;若根结点关键值大于待查值,则进入左子树重复此步骤,否则,进入右子树重复此步骤;若在查找过程的中遇到二叉排序树的叶子结点时,还没有找到待查结点,则查找不成功。

if(t==NULL)

returnNULL;

else{

if(t->data==x)

returnt;

if(xdata)

return(Bsearch(t->lchild,x));

else

return(Bsearch(t->rchild,x));

}

二叉排序树递归查找算法流程图

2、非递归查找算法

查找过程是从根结点开始逐层向下进行的。

并定义一个标记量记录是否找到结点。

Bstnode*searchBST(Bstnode*t,intx)

{

Bstnode*p;intflag=0;p=t;//定义*p结点用于逐层查找,丛根结点开始查找

while(p!

=NULL)//二叉排序树不为空

{

if(p->key==x)//查找成功

{

printf("该结点值存在!

");flag=1;

break;

}

//查找不成功,到下一层继续查找

if(xkey)

p=p->lchild;//查找左子树

else

p=p->rchild;//查找右子树

}

if(flag==0)

{

printf("找不到值为%d的结点!

",x);

p=NULL;

}

returnp;

}

3、插入算法

从根结点开始,根据比较规则,逐一与待插入结点的值比较,查找到插入结点在二叉排序树中的未来位置,然后插入该结点。

将一个关键字的值为x的结点s插入到二叉排序树中,方法如下:

(1)若二叉排序树为空,则关键字值为x的结点s成为二叉排序树的根。

(2)若二叉排序树非空,则将x与二叉排序树的根进行比较,如果x的值等于根结点关键字的值,则停止插入;如果x的值小于根结点关键字的值,则将x插入左子树;如果x的值大于根结点关键字的值,则将x插入右子树。

在左右子树中插入方法与整个二叉排序树相同。

Bstnode*InsertBST(Bstnode*t,intx)//插入关键值为x的元素

{

Bstnode*s,*p,*f;//*s为待插结点,*p为逐层查找结点,*f为待插结点的父结点

p=t;

while(p!

=NULL)

{

f=p;//查找过程中,f指向*p的父结点

if(x==p->key)//若二叉树中已有关键值为x的元素,无需插入

returnt;

if(xkey)

{

p=p->lchild;

}

else

{

p=p->rchild;

}

}

s=(Bstnode*)malloc(sizeof(Bstnode));

s->key=x;

s->lchild=NULL;s->rchild=NULL;

if(t==NULL)//原树为空,新结点作为二叉排序树的根

returns;

if(xkey)

f->lchild=s;//新结点作为*f左孩子

else

f->rchild=s;//新结点作为*f右孩子

returnt;

}

4、二叉排序树的生成算法

建立二叉排序树,就是反复在二叉排序树中插入新的结点。

插入的原则是如果待插入结点的值小于根结点的值,则插入到左子树中,否则插入到右子树中。

大致方法是:

首先建一棵空二叉排序树,然后逐个读入元素,每读入一个元素,就建一个新结点,并调用上述二叉排序树的插入算法,将新结点插入到当前已生成的二叉排序树中,最终生成一棵二叉排序树。

Bstnode*CreateBST()

{

Bstnode*t;

intkey;

t=NULL;//设置二叉排序树的初态为空

scanf("%d",&key);

while(key!

=endflag)

{

t=InsertBST(t,key);

scanf("%d",&key);

}

returnt;

}

5、中序遍历算法

voidInorder(Bstnode*t)

{

if(t!

=NULL)

{

Inorder(t->lchild);

Printf(“%4d\n”,t->key);

Inorder(t->rchild);

}

}

先遍历左孩子,再遍历父结点,最后遍历右孩子。

由于是对一个二叉排序树进行中序遍历,遍历结果则是一个有序序列。

6、删除算法

(1)待删除结点*p无左孩子,也无右孩子,则*p的父结点对应的孩子指针置空;

(2)待删除结点*p有左孩子,无右孩子,则*p的左孩子替代自己;

(3)待删除结点*p无左孩子,有右孩子,则*p的右孩子替代自己;

(4)待删除结点*p有左孩子,也有右孩子,本课程(数据结构与算法)给出了两种法:

方法一:

首先找到待删结点*p的前驱结点*s,然后将*p的左子树改为*p父结点的左子树,而*p的右子树改为*s的右子树:

f->lchild=p->lchild;

s->rchild=p->rchild;

free(p);

方法二:

首先找到待删结点*p的前驱结点*s,然后用结点*s的值替代结点*p的值,再将结点*s删除,结点*s的原左子树改为*s的双亲结点*q的右子树:

p->data=s->data;

q->rchild=s->lchild;

free(s);

我采用的是第二种算法。

7、注意事项:

其中,某些函数顺序一定不能颠倒。

例如建立二叉排序树函数一定是在插入算法之后。

编写完基本操作算法后,为最后主函数的输出模块作准备,又分别写了递归查找模块、插入模块、删除模块、显示模块。

四、上机调试

1、语法错误及修改:

在编写程序时,很容易出现分号漏写和括号不匹配的现象,以及缺少返回值的问题。

例如:

在编写非递归查找算法时:

Bstnode*searchBST(Bstnode*t,intx)

{

Bstnode*p;intflag=0;

p=t;

while(p!

=NULL)

{

if(p->key==x)

{

printf("找到了!

");flag=1;

returnp;break;

}

if(xkey)

p=p->lchild;

else

p=p->rchild;

}

if(flag==0)

{

printf("找不到值为%d的结点",x);

returnNULL;

}

}

结果编译时出现了警告warning:

'searchBST':

notallcontrolpathsreturnavalue

然后我做了改动,改后程序如下:

Bstnode*searchBST(Bstnode*t,intx)

{

Bstnode*p;intflag=0;p=t;

while(p!

=NULL)

{

if(p->key==x)

{printf("该结点值存在!

");flag=1;

break;

}

if(xkey)

p=p->lchild;

else

p=p->rchild;

}

if(flag==0)

{printf("找不到值为%d的结点!

",x);

p=NULL;

}

returnp;

}

将NULL值赋给指针p,再在程序结尾返回p,此时,编译结果就正确了!

2、程序输出调整:

在递归查找算法(Bsearch)中针对查找结果如何,没有用明确的输出函数表示出来。

于是我添加了一个递归查找模块如下:

search_Bitree(Bstnode*t)

{

ints;

Bstnode*p;

printf("\n请输入要查找的结点的值:

");

scanf("%d",&s);

if(s!

=0)

{p=Bsearch(t,s);

if(p==NULL)

printf("该结点值不存在!

\n");

else

printf("找不到值为%d的结点!

\n",s);

}

}

这样主函数便可直接调用该函数来实现查找过程。

3、时间和空间性能分析:

由于二叉排序树的中序遍历序列为一个递增的有序序列,这样可以将二叉排序树看作是一个有序表。

其查找过程:

若查找成功,则是从根结点出发走了一条从根到某个结点的路径;若查找不成功,则是从根结点出发走了一条从根到某个叶子结点的路径。

和关键字比较次数不超过二叉排序树的深度。

二叉排序树的平均查找长度与其形态有关。

最坏情况是具有n个结点的单支树,其平均查找长度与顺序查找相同,为(n+1)/2;即平均查找长度的数量级为O(n)。

在最好情况下,二叉排序树形态均匀,它的平均查找长度与二分查找相似,大约是log2n,其平均查找长度的数量级为O(log2n)。

4、经验与体会:

由于该设计问题是对数据结构与算法课程中二叉树章节的灵活应用,所以课本给了我很大的帮助,结合所学知识以及对二叉排序树的理解,来编写该程序。

五、测试结果及分析

1、

建立如图(a)的二叉排序树,输入的数据依次为:

4524531228900(0为输入结束符),屏幕(图

(1))上显示的是横置的二叉树

图(a)图(b)

(1)

2、选择方框中给定的项目(输入0~4中任意数)。

若输入错误会有提示,可以重新输入。

(2)

3、输入1,则出现查找菜单。

选择查找方法。

图(3)

 

4、在查找菜单选1,则进行递归查找。

图(4)

5、同理,若选择2,则进行非递归查找。

查找的值存在与否都会有显示。

图(5)

 

6、选2,则进行插入操作。

插入关键值为13的结点后,显示如图(6),即插入13后的横置的二叉排序树(图(c)——图(d))。

 

 

图(c)图(d)

 

图(6)

 

7、选3,进行删除操作。

删除关键值为24的结点后,显示如图(7),即删除24后的横置的二叉排序树(图(e)——图(f))

 

 

图(e)图(f)

 

图(7)

 

8、选4则显示详细信息。

如图(8)所示,按中序遍历(从小到大)依次输出,并显示每个结点的孩子情况,最后打印出横置的二叉排序树。

图(8)

9、选0则退出程序。

图(9)

 

六、用户使用说明

用户可以根据本程序运行过程中出现的提示性语句来进行操作。

要注意括号中的提示,若没有按照提示输入的话,程序可能将无法继续进行下去。

例如开始输入数据时直到输入0时才算结束输入,从而进行下一步操作。

在遇到选项时,选择错误会给你重新选择的机会。

提示:

进行一系列插入删除等操作后,可以选择显示项来显示最后的二叉排序树状态(二叉排序树显示的是中序遍历后的序列)。

七、

参考文献

(1)王昆仑.李红.数据结构与算法.北京:

中国铁道出版社,2007.6

(2)王昆仑.李红.数据结构与算法试验指导,2009

(3)谭浩强.c程序设计.北京:

清华大学出版社,2005.7

(4)严蔚敏.数据结构:

c语言版.北京:

清华大学出版社,2002

(5)耿国华.等.数据结构:

用c语言描述.北京:

高等教育出版社,2004

八、附录

#include"stdio.h"

#include"malloc.h"

#include"stdlib.h"

#defineendflag0//定义endflag为关键字输入结束的标志

//二叉排序树的结点结构

typedefstructnode

{

intkey;//关键字项

structnode*lchild,*rchild;//左右孩子指针

}Bstnode;

//二叉排序树的查找算法之一(递归)

Bstnode*Bsearch(Bstnode*t,intx)

{

if(t==NULL)//二叉排序树为空,查找失败

returnNULL;

else{

if(t->key==x)//查找成功,返回当前结点

returnt;

if(xkey)

return(Bsearch(t->lchild,x));//进入左子树递归查找

else

return(Bsearch(t->rchild,x));//进入右子树递归查找

}

}

//递归查找函数(显示查找结果)

search_Bitree(Bstnode*t)

{

ints;//定义待查结点的关键值

Bstnode*p;//定义待查的结点

printf("\n请输入要查找的结点的值:

");

scanf("%d",&s);

if(s!

=0)

{

p=Bsearch(t,s);//递归查找

if(p!

=NULL)

printf("该结点值存在!

\n");

else

printf("找不到值为%d的结点!

\n",s);

}

}

//二叉排序树的查找算法之二(非递归)

Bstnode*searchBST(Bstnode*t,intx)

{

Bstnode*p;intflag=0;p=t;//定义*p结点用于逐层查找,丛根结点开始查找

while(p!

=NULL)//二叉排序树不为空

{

if(p->key==x)//查找成功

{

printf("该结点值存在!

");flag=1;

break;

}

//查找不成功,到下一层继续查找

if(xkey)

p=p->lchild;//查找左子树

else

p=p->rchild;//查找右子树

}

if(flag==0)

{

printf("找不到值为%d的结点!

",x);

p=NULL;

}

returnp;

}

//二叉排序树的结点插入算法

Bstnode*InsertBST(Bstnode*t,intx)//插入关键值为x的元素

{

Bstnode*s,*p,*f;//*s为待插结点,*p为逐层查找结点,*f为待插结点的父结点

p=t;

while(p!

=NULL)

{

f=p;//查找过程中,f指向*p的父结点

if(x==p->key)//若二叉树中已有关键值为x的元素,无需插入

returnt;

if(xkey)

{

p=p->lchild;

}

else

{

p=p->rchild;

}

}

s=(Bstnode*)malloc(sizeof(Bstnode));

s->key=x;

s->lchild=NULL;s->rchild=NULL;

if(t==NULL)//原树为空,新结点作为二叉排序树的根

returns;

if(xkey)

f->lchild=s;//新结点作为*f左孩子

else

f->rchild=s;//新结点作为*f右孩子

returnt;

}

//中序遍历(递归法)

voidInorder(Bstnode*t)

{

if(t!

=NULL)//若二叉树不空

{

Inorder(t->lchild);//遍历左孩子

printf("%4d",t->key);

Inorder(t->rchild);//遍历右孩子

}

}

//二叉排序树的结点删除算法

Bstnode*DeleteBST(Bstnode*t,intk)//删除关键值为k的元素

{

Bstnode*p,*f,*s,*q;//*p为待删结点,*f为*p父结点,*s为*p的中序前驱结点,*q为*s的父结点

p=t;f=NULL;//从根结点开始查找,并将*f置空

//查找关键值为k的待删结点*p

while(p!

=NULL)

{

if(p->key==k)break;//若找到,则退出循环

f=p;//未找到时将*f替代*p,*p则下移进入左右子树继续查找

if(p->key>k)

p=p->lchild;

else

p=p->rchild;

}

if(p==NULL)returnt;//若找不到,则返回原二叉排序树的根指针

//查找成功后,对*p的删除过程

if(p->lchild==NULL||p->rchild==NULL)//若*p无左子树或无右子树

{

if(f==NULL)//若*p是原二叉排序树的根

{

if(p->lchild==NULL)t=p->rchild;//无左孩子,右孩子做根

elset=p->lchild;

}//无右孩子,左孩子做根

elseif(p->lchild==NULL)//若*p无左子树

{

if(f->lchild==p)f->lchild=p->rchild;//p是*f的左孩子

elsef->rchild=p->lchild;//p是*f的右孩子

}

else//若*p无右子树

{

if(f->lchild==p)f->lchild=p->lchild;//p是*f的左孩子

elsef->rchild=p->lchild;//p是*f的右孩子

}

free(p);

}

else//若*p有左右子树

{

q=p;s=p->lchild;

while(s->rchild)//在*p的左子树中查找最右下结点(即其中序前驱结点)

{

q=s;s=s->rchild;

}

if(q==p)q->lchild=s->lchild;

elseq->rchild=s->lchild;

p->key=s->key;//将*s的值赋给*p

free(s);

}

returnt;

}

//插入函数(显示插入结果)

Bstnode*insert_Bitree(Bstnode*t)

{

ints;//定义待插结点的关键值

Bstnode*p;//定义待插的结点

printf("\n");

printf("请输入要插入的结点的值:

");

scanf("%d",&s);

if(s!

=0)

{

p=Bsearch(t,s);

if(p==NULL)

{

t=InsertBST(t,s);

printf("插入结点中序遍历后的二叉排序树:

\n");

Inorder(t);

printf("\n二叉排序树的根为:

%d\n",t->key);

}

elseprintf("该结点值存在,不插入!

\n");

}

returnt;

}

//删除函数(显示删除结果)

Bstnode*delete_Bitree(Bstnode*t)

{

ints;//定义待删结点的关键值

Bstnode*p;//定义待删的结点

printf("\n请输入要删除的结点的值:

");

scanf("%d",&s);

if(s!

=0)

{

p=Bsearch(t,s);

if(p==NULL)

printf("找不到值为%d的结点!

",s);

else

{

t=DeleteBST(t,s);

printf("删除结点后中序遍历的二叉排序树:

\n");

Inorder(t);

printf("\n二叉排序树的根为:

%d\n",t->key);

}

}

returnt;

}

//设置二叉排序树的初值

Bstnode*Creat

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1