如果b≥data(p),将b插入右子树中;
左、右子树的插入方式与二叉排序树的插入方式相同。
不断调用上述的插入过程,直到所有待排序序列均排入后,就形成一棵二叉排序树。
由此可见,建立二叉排序树就是多次调用二叉排序树的插入算法。
2.2二叉排序树的中序遍历
中序遍历二叉树算法的框架是:
若二叉树为空,则空操作;否则
中序遍历左子树(L);
访问根结点(V);
中序遍历右子树(R)。
中序遍历二叉树也采用递归函数的方式,先访问左子树,然后访问根结点,最后访问右子树.直至所有的结点都被访问完毕。
2.3二叉排序树中元素的查找
在二叉排序树上进行查找,是一个从根结点开始,沿某一个分支逐层向下进行比较判等的过程。
它可以是一个递归的过程。
假设我们想要在二叉排序树中查找关键码为x的元素,查找过程从根结点开始。
如果根指针为NULL,则查找不成功;否则用给定值x与根结点的关键码进行比较;如果给定值等于根结点的关键码,则查找成功,返回查找成功的信息,并报告查找到的结点地址。
如果给定值小于根结点的关键码,则继续递归查找根结点的左子树;否则,递归搜索根结点的右子树。
2.4二叉排序树中元素的删除
对于二叉排序树,删去树上的一个结点相当于删去有序序列中的一个记录,只要在删除某个结点之后依旧保持二叉排序树的特性即可。
假设在二叉排序树上被删除结点为*p(指向结点的指针是p),其双亲结点为*f(结点指针为f),且不失一般性,可设*p是*f的左孩子,若*p结点为叶子结点,即p和l均为空,只需修改其双亲结点指针即可。
若*p结点只有左子树或者只有右子树,只要令左子树或右子树直接成为其双亲结点即可。
若左子树和右子树都不为空,令*p的直接前驱替代*p,然后从二叉排序树中删除它的直接前驱,即可。
2.5总体设计流程图
4.1程序总体流程图
3.详细设计
∙Tnode的声明
typedefstructTnode{
intdata;
structTnode*lchild,*rchild;
}*node,BSTnode;
∙searchBST的声明
searchBST(nodet,intkey,nodef,node*p)
{
if(!
t)
{*p=f;return(0);}
else
if(key==t->data){*p=t;return
(1);}
else
if(keydata)searchBST(t->lchild,key,t,p);
else
searchBST(t->rchild,key,t,p);
}
∙insertBST的声明
insertBST(node*t,intkey)
{
nodep=NULL,s=NULL;
if(!
searchBST(*t,key,NULL,&p))
{
s=(node)malloc(sizeof(BSTnode));
s->data=key;
s->lchild=s->rchild=NULL;
if(!
p)*t=s;
else
if(keydata)p->lchild=s;
elsep->rchild=s;
return
(1);
}
else
return(0);
}
∙inorderTraverse类的声明
inorderTraverse(node*t)
{
if(*t){
if(inorderTraverse(&(*t)->lchild))
printf("%d",(*t)->data);
if(inorderTraverse(&(*t)->rchild));
}
return
(1);
}
∙Delete类的声明
nodeDelete(nodet,intkey)
{
nodep=t,q=NULL,s,f;
while(p!
=NULL)
{
if(p->data==key)
break;
q=p;
if(p->data>key)p=p->lchild;
else
p=p->rchild;
}
if(p==NULL)
returnt;
if(p->lchild==NULL)
{
if(q==NULL)t=p->rchild;
else
if(q->lchild==p)q->lchild=p->rchild;
else
q->rchild=p->rchild;
free(p);
}
else{
f=p;
s=p->lchild;
while(s->rchild)
{
f=s;
s=s->rchild;
}
if(f==p)f->lchild=s->lchild;
elsef->rchild=s->lchild;
p->data=s->data;
free(s);
}returnt;
}
3.1中序遍历模块
系统将提示用户输入新添加的数据,输入结束后进行中序遍历
关键代码:
inorderTraverse(node*t)/*中序遍历函数*/
{
if(*t){
if(inorderTraverse(&(*t)->lchild))/*中序遍历根的左子树*/
printf("%d",(*t)->data);/*输出根结点*/
if(inorderTraverse(&(*t)->rchild));/*中序遍历根的右子树*/
}
return
(1);
}
3.2删除模块
系统将对用户输入的数进行查找,查找到之后删除此数,并对全部数据进行中序遍历。
关键代码:
nodeDelete(nodet,intkey)/*删除函数*/
{
nodep=t,q=NULL,s,f;
while(p!
=NULL)/*查找要删除的点*/
{
if(p->data==key)
break;
q=p;
if(p->data>key)p=p->lchild;
else
p=p->rchild;
}
if(p==NULL)
returnt;/*查找失败*/
if(p->lchild==NULL)/*p指向当前要删除的结点*/
{
if(q==NULL)t=p->rchild;/*q指向要删结点的父母*/
else
if(q->lchild==p)q->lchild=p->rchild;/*p为q的左孩子*/
else
q->rchild=p->rchild;/*p为q的右孩子*/
free(p);
}
else{/*p的左孩子不为空*/
f=p;
s=p->lchild;
while(s->rchild)/*左拐后向右走到底*/
{
f=s;
s=s->rchild;
}
if(f==p)f->lchild=s->lchild;/*重接f的左子树*/
elsef->rchild=s->lchild;/*重接f的右子树*/
p->data=s->data;
free(s);
}
returnt;}
4编码与调试
首先进入VC++6.0,打开工程zjr.dsw,然后进入源程序,也可以不打开工程,直接双击zjr文件夹下的zjr.exe文件即可运行程序。
4.1顺序存储
图7.1.1打开程序,成功显示提示信息
图7.1.2输入数据,以0结束输入,操作成功
图7.1.3功能1:
中序输出数据,操作成功
图7.1.4功能2:
删除数据,显示提示信息,操作成功
图7.1.5删除数据,操作成功
图7.1.6重复执行程序,操作成功
4.2二叉链表存储
图7.2.1打开程序,显示提示信息,操作成功
图7.2.2功能1:
输入数据,进行中序遍历,操作成功
图7.2.3功能2:
输入数据,进行删除,操作失败,因为没有此数据,显示错误信息
图7.2.4功能2:
输入数据,进行中序遍历,操作成功
通过上述测试,本系统实现了二叉树的生成,中序遍历,查找删除功能,能避免数据的输入错误等。
验证结果正确,说明其符合算法设计的要求:
(1)正确性、可读性、健壮性、效率与低储存量需求.要写一个优质的算法,就必须考虑其时间复杂度(它表示随问题的规模n的增大,算法执行时间的增长率和f(n)的增长率相同)和空间复杂度。
遍历二叉树的算法中的基本操作是访问结点,则不论按哪一次次序进行遍历,对含n个结点的二叉树,其时间复杂度都为O(n)。
所需空间为遍历过程中栈的最大容量,即树的深度,最坏情况下为n,则空间复杂度也为O(n)。
5总结
这次课程设计使我对数据结构认识深刻了许多,其中最深刻的是我理解了用二叉链表结构存储实现二叉排序树,同时也加深了对二叉树的理解。
本课程设计实现了二叉排序树的创建、中序遍历、删除二叉排序树中某个结点,。
在进行搜索时,还可以采用更好的搜索结构即动态搜索结构。
当没有找到时,可以将其插入,而不是仅仅提示未找到。
通过这一次的课程设计,我已经会用二叉链表存储结构实现对二叉排序树的的创建,中序遍历,查找和某个删除结点等基本操作。
但我同时也发现了自己许多不足之处。
首先,对数据结构的掌握还不够。
虽然完成了程序,但是只用到了基本的结点以及链表,在数据结构的选择上避重就轻,覆盖面较小,不能很好的体现各种数据结构的掌握度,同时也缺少了适当的锻炼,在这方面还需要自己主动去提高。
其次,在程序整体的设计上还不够完善,各模块可以适当增加内容,界面还可以更加的人性化些,这样整个程序才具有更强的美观性与实用性。
总而言之,这次课程设计给了我很大启发,我明白了,不管遇到什么问题,只要抓住根源,不气馁,从不同方面去攻破它,终究会成功,生活也是如此。
在今后的工作、学习中我将认真总结经验教训,努力使自己成为一名技术过硬、工作严谨、思维活跃的工程人员,为提高人们的生活质量做出更大的贡献。
心得体会
课程设计结束了,在这次的课程设计中不仅检验了我所学的知识,也培养了我如何去把握一件事情,如何去做一件事情,课程设计是我们专业课程知识综合应用的实践训练是我们迈向社会,从事职业工作前一个必不可少的过程。
我这次设计的科目是数据结构,数据结构,是一门研究非数值计算的程序设计问题中计算机的操作对象(数据元素)以及它们之间的关系和运算等的学科,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。
这次通过对二叉排序树的深入研究,我又一次的温习了c,c++和数据结构的有关知识,尽管这中间经历好多困难,但我通过查找多种资料,利用网络优势,完成了任务。
我这次课程设计代码中主要使用了链表的循环和遍历这两种操作。
循环链表是单链表的另一种形式。
遍历是指沿着某条收索路线,依次对树中每个节点均作一次且仅作一次访问。
通过这次的课程设计,更是让我深刻认识到自己在学习中的不足,同时也找到了克服这些不足的方法,这是一笔很大的资源。
在以后的学习中,我们应该利用更多的时间去上机实验,加强自学的能力、多编写程序,相信不久以后我们的编程能力都会有很大的提高,能创作出更多更完善更有新意的作品出来。
参考文献
[1]谭浩强编著.程序设计.北京:
清华大学出版社,2000
[2]王珊珊,张志航编著.C++程序设计教程.北京:
机械工业出版社,2011
[3]严蔚敏,吴伟民编著.数据结构.北京:
清华大学出版社,2001
全部代码
二叉链表结构c
#include
#include
typedefstructTnode{
intdata;/*输入的数据*/
structTnode*lchild,*rchild;/*结点的左右指针,分别指向结点的左右孩子*/
}*node,BSTnode;
searchBST(nodet,intkey,nodef,node*p)/*查找函数*/
{
if(!
t)
{*p=f;return(0);}/*查找不成功*/
else
if(key==t->data){*p=t;return
(1);}/*查找成功*/
else
if(keydata)searchBST(t->lchild,key,t,p);/*在左子树中继续查找*/
else
searchBST(t->rchild,key,t,p);/*在右子树中继续查找*/
}
insertBST(node*t,intkey)/*插入函数*/
{
nodep=NULL,s=NULL;
if(!
searchBST(*t,key,NULL,&p))/*查找不成功*/
{
s=(node)malloc(sizeof(BSTnode));
s->data=key;
s->lchild=s->rchild=NULL;
if(!
p)*t=s;/*被插结点*s为新的根结点*/
else
if(keydata)p->lchild=s;/*被插结点*s为左孩子*/
elsep->rchild=s;/*被插结点*s为右孩子*/
return
(1);
}
else
return(0);/*树中已有关键字相同的结点,不再插入*/
}
inorderTraverse(node*t)/*中序遍历函数*/
{
if(*t){
if(inorderTraverse(&(*t)->lchild))/*中序遍历根的左子树*/
printf("%d",(*t)->data);/*输出根结点*/
if(inorderTraverse(&(*t)->rchild));/*中序遍历根的右子树*/
}
return
(1);
}
nodeDelete(nodet,intkey)/*删除函数*/
{
nodep=t,q=NULL,s,f;
while(p!
=NULL)/*查找要删除的点*/
{
if(p->data==key)
break;
q=p;
if(p->data>key)p=p->lchild;
else
p=p->rchild;
}
if(p==NULL)
returnt;/*查找失败*/
if(p->lchild==NULL)/*p指向当前要删除的结点*/
{
if(q==NULL)t=p->rchild;/*q指向要删结点的父母*/
else
if(q->lchild==p)q->lchild=p->rchild;/*p为q的左孩子*/
else
q->rchild=p->rchild;/*p为q的右孩子*/
free(p);
}
else{/*p的左孩子不为空*/
f=p;
s=p->lchild;
while(s->rchild)/*左拐后向右走到底*/
{
f=s;
s=s->rchild;
}
if(f==p)f->lchild=s->lchild;/*重接f的左子树*/
elsef->rchild=s->lchild;/*重接f的右子树*/
p->data=s->data;
free(s);
}
returnt;
}
voidmain()
{
nodeT=NULL;
intnum;
ints=0,j=0,i=0;
intch=0;
nodep=NULL;
printf("输入一串数,每个数以空格分开:
");
do{
scanf("%d",&num);
if(!
num)printf("完成输入!
\n");
elseinsertBST(&T,num);
}while(num);
printf("\n1:
中序输出");
printf("\n2:
输入元素");
while(ch==ch)
{
scanf("%d",&ch);
switch(ch){
case0:
exit(0);/*0--退出*/
case1:
printf("中序遍历输出结果为:
\n");
inorderTraverse(&T);/*1--中序遍历*/
break;
case2:
printf("输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结点,并作中序遍历,否则输出无x:
");
scanf("%d",&num);/*3--删除某个结点*/
if(searchBST(T,num,NULL,&p))
{
T=Delete(T,num);
printf("删除成功!
中序遍历输出:
\n");
inorderTraverse(&T);
}
else
printf("无%d",num);
break;
default:
printf("无此结点\n");
break;/*输入无效字符*/
}
}
}
二叉链表结构c++
#include
usingnamespacestd;
classnode
{
public:
node(inti):
data(i),left(NULL),right(NULL){}
voidinorder(node*&root)//中序遍历,符合升序输出
{
if(root!
=NULL)
{
inorder(root->left);
cout<data<<'';
inorder(root->right);
}
}
voidinsert(node*&ptr,intitem)//在查找树中插入元素
{
if(ptr==NULL)
ptr=newnode(item);
elseif(itemdata)
insert(ptr->left,item);
elseinsert(ptr->right,item);
}
node*find(node*&ptr,intitem)//在查找树中查找元素,找到返回所在结点指针,找不到返回空指针。
{
if(ptr==NULL)
returnNULL;
if(ptr->data==item)
returnptr;
elseif(itemdata)
find(ptr->left,item);
elsefind(ptr->right,item);
}
node*&findy(node*&ptr,intitem)//在查找树中查找肯定存在的元素,并返回其引用
{
if(ptr->data==item)
returnptr;
elseif(itemdata)
findy(ptr->left,item);
elsefindy(ptr->right,item);
}
node*rl(){returnleft;}
node*rr(){returnright;}
voiddele(node*&ptr)//删除值为item所在结点
{
if(ptr->rl()==NULL&&ptr->rr()==NULL)
ptr=NULL;
elseif(ptr->rr()==NULL)
ptr=ptr->rl();
else
ptr=