课程设计说明书(数据结构课程设计)专业:网络工程课程名称:数据结构课程设计班级:网络B11-1设计题目:线索二叉树的应用设计时间:2013-2-25至2013-3-8评语:_____________________________________________________________________________________________________________________________________________________________________________________________________评阅成绩:____评阅教师:__一、问题描述与需求分析1、问题描述本实验的问题是建立一个线索二叉树,并实现线索二叉树的插入、删除、恢复线索等功能。2、功能需求分析本程序要实现的功能是:1.线索二叉树的建立。2.线索二叉树的插入。3.线索二叉树的删除。4.线索二叉树的恢复。想要完成上面的功能,我们首先是要知道上面是线索二叉树。我们可以从数据结构的书上找到答案,利用二叉链表的空指针域将空的左孩子指针域改为指向其前驱,空的右孩子指针域改为指向其后继。这种改变指向的指针称为线索,加上了线索的二叉链表称为线索链表。N个结点的二叉链表中含有n+1个空指针域。利用二叉链表中的空指针域,存放指向结点的在某种遍历次序下的前驱和后继结点的指针,这种加上了线索的二叉链表称为线索链表。相应的二叉树称为线线索二叉树。根据线索二叉树性质的不同,线索二叉树可以分为前序线索二叉树,中序线索二叉树和后续线索二叉树三种,此次课程设计中使用的是中序线索二叉树。二、概要设计1、总体设计思路首先就是要建立一个二叉树,然后再对二叉树进行线索化。线索链表中的结点结构为:其中:线索二叉树及其存储结构如在线索树上进行遍历,只需先找到序列中的第一个结点,然后依次找结点后继为空时而止。以上图为例子说明在线索树中查找结点的后继。树中所有叶子的结点是线索,则右链域直接指示结点的后继,如结点b的后继为结点*。树中所有非终端结点的右链均为指针,则无法由此得到后继的信息。然而根据中序遍历的规律可知,结点的后继应是遍历其右子树时访问的第一个结点,即右子树最左下的结点。列入在找结点*的后继时,首先沿右指针找到其右子树的根结点“—”,然后顺其左指针往下直至其左标志为1的结点,即为结点*的后继,在图中是结点c。反之,在中序结点中找结点前驱的规律是:若其左标志为“1”,则左链为线索,指示其前驱,否则遍历左子树时最后访问的一个结点(左子树最右下的结点)为其前驱。NNNNYYYYY开始定义二叉树建立二叉树选择菜单输入i(1--5)I=1I=2I=4I=5I=3中序输出二叉树插入结点并线索化删除结点并线索haunted输出线索二叉树二叉树的线索化程序流程图2、模块简介本程序有五个模块功能。每个模块功能均实现特定的功能。1.二叉树的建立:中序输入二叉树,为程序提供数据,输入的时候空域用@表示。2.进行二叉树的线索化:按中序遍历顺序将二叉树线索化,只需要在遍历的过程中将二叉树的每个结点的空的左右孩子的指针域分别修改为指向其前驱和后继,若其左子树为空,则将其左孩子域线索化,使其左孩子指针lchild指向其后继,并且ltag置1.3插入操作:输入要插入的结点信息,然后再查找要插入结点的位置,插入新结点。4删除操作,确定要删除的结点,查找出要删除的结点,找到之后会显示ltag和rtag信息。5输出线索二叉树,输出的线索二叉树为经过处理的二叉树。三、详细设计1、数据结构设计程序采用线索链表做存储结构。程序中有二叉树的建立,插入,删除,恢复线索,这些操作都是基于线索链表作的。2、算法分析与实现结束二叉树的建立:建立一个二叉树,需要按照某种顺序依次输入二叉树中的结点,且该输入顺序必须隐含结点间的逻辑结构信息。这种建立的方法,按全二叉树的层次顺序,一次输入结点信息建立二叉链表的过程。以@表示空结点,以#表示输入结束的标志,。一次输入结点信息,若其不是虚结点,则建立一个新结点。若新结点是第一个结点,则令其为跟结点,否则将新结点作为孩子链接到它的父亲结点上。在函数中设置一个队列,该队列是一个指针类型的数组,保存以输入的结点的地址。使队列指针front指向当前与孩子建立链接的父亲结点,,队尾指针rear指向当前输入的结点。若rear为偶数,则该结点为父结点的左孩子,若rear为奇数,则为父结点的右孩子。若父结点或孩子结点为虚结点,则无需链接,若父结点与其两个孩子结点链接完毕,则使front指向下一个等待链接的父结点。算法实现:Bithptr*Q[maxsize];//建队为指针类型Bithptr*CreatTree(){front=1;rear=0;//置空队if(ch!='@')//不是虚结点�.则建立结点s=(Bithptr*)malloc(sizeof(Bithptr));s-data=ch;s-lchild=NULL;s-rchild=NULL;s-rtag=0;s-ltag=0;if(s!=NULL&&Q[front]!=NULL)//孩子和双亲结点都不是虚结点if(rear%2==0)Q[front]-lchild=s;elseQ[front]-rchild=s;if(rear%2==1)front++;//结点*Q[front]的两个孩子处理完�front+1线索化算法:线索过程需要按照一定的顺序进行,此次程序使用的是中序遍历,所以线索化也将使用中序线索化。按照某种遍历顺序将二叉树线索化,只需在遍历的过程中将二叉树的每个结点空的左右孩子的指针分别修改为指向其前驱和后继。若其左右树为空,则将其左孩子域线索化,使其左孩子指针lchild指向其后继,并且ltag为1.要实现线索化,就要知道结点*pre是*p的前驱,而*p是*pre的后继。这样,当遍历到结点*p时,可以进行,若*P有空指针域,则将相应的标志为1,;若*p的左线索标志已经建立�(p-ltag==1),则可使其前驱线索化,令p-lchild=pre;若*pre的左线索标志已经建立(pre-rtag==1),则可使其后继线索化,令pre-rchild=p;算法实现;voidPreThread(Bithptr*root){PreThread(p-lchild);//左子树线索化if(pre&&pre-rtag==1)pre-rchild=p;//前驱结点后继线索化if(p-lchild==NULL)p-ltag=1;p-lchild=pre;if(p-rchild==NULL)//后继结点前驱线索化p-rtag=1;pre=p;PreThread(p-rchild);}}插入结点函数:在书中插入一个结点,必须要以固定的规则来进行插入。本程序中树的输出使用了中序输出的方法,所以插入的时候使用的规则就是以中序输出为顺序,先查找到一个点,再将要插入的结点,作为该结点的前驱插入树中。如中序为:→d→b→e→a→f→c→g。插入的结点为:b,则插入后的顺序为→d→w→b→e→a→f→c→g。使用查找孩子指针函数来查找结点位置的指针。在查找的过程中要处理好线索指针和孩子指针的关系,不能将线索也当做孩子来处理了。并且在循环的判断中,且不能使用以前的空位结束语句,而是要用标志域来进行判断。在查找的过程中,考虑到树的递归性质,所以讲查找函数也设置成递归函数。算法实现:Bithptr*SearchChild(Bithptr*point,charfindnode){Bithptr*point1,*point2;if(point!=NULL){if(point-data==findnode)returnpoint;//找到结点的信息�.返回指针elseif(point-ltag!=1){//判断是否有左子树point1=SearchChild(point-lchild,findnode);//递归if(point1!=NULL)returnpoint1;}if(point-rtag!=1){//判断是否有右子树point2=SearchChild(point-rchild,findnode);//递归if(point2!=NULL)returnpoint2;}returnNULL;}elsereturnNULL;}在一棵树种插入一个结点,因为插入的位置不同,就对应着不同的插入情况。当插入结点有左孩子时:找到左孩子的最右下结点将待插的结点插为其结点的右孩子。当插入结点没有左孩子时:直接将带插的结点插为其结点的左孩子。算法实现:�当结点有左子树时�.p2=child;child=child-lchild;while(child-rchild&&child-rtag==0)//左子树的最右下结点child=child-rchild;p1-rchild=child-rchild;//后继线索化p1-rtag=1;child-rtag=0;child-rchild=p1;//连接结点p1-lchild=child;//前驱线索化p1-ltag=1;�当结点没左孩子的时�.p1-lchild=child-lchild;//前驱化child-ltag=0;p1-ltag=1;child-lchild=p1;p1-rchild=childp1-rtag=1;删除结点函数:要在函数中删除一个结点,有几种不同的情况,在删除结点之前要先找到要删除的结点,调用查找孩子结点的函数Bithptr*SearchChild(Bithptr*point,charfindnode),找到其结点指针的指针。删除过程中设计的指针变换需要父亲结点的指针,所以就调用查找父亲结点的函数Bithptr*SearchPre(Bithptr*point,Bithptr*child)来查找该结点的父亲结点的指针。删除的过程中有不同的情况。1当要删除结点为父亲的左孩子时若要删除结点没有左右孩子:则直接删除;若要删除结点有左孩子没有右孩子:则要删除结点的左孩子给父亲结点的左孩子;若要删除结点有右孩子没有左孩子:则将要删除结点的右孩子给父亲结点的左孩子;若要删除结点左右孩子都有:将要删除结点的左子树的右子树接到孩子结点的右子树的最左下结点的左子树�.再将孩子结点的右子树接到孩子结点左子树的右子树。2当要删除结点是父亲结点的右孩子时若要删除结点没有左右孩子。则直接将空给头指针。若要删除结点有左孩子没右孩子,则将孩子结点的左孩子作为头结点。若要删除结点有右孩子没左孩子,则将孩子结点的右孩子作为头结点。若要删除结点左右孩子都有,则将孩子结点的左孩子作为头结点,将孩子结点的右子树插入孩子结点的左子树的最右下结点的右子树。算法实现:�只列出要删除结点是父结点的左子树的情况�.要删除结点无左右pre-lchild=child-lchild;pre-ltag=1;if(child-lchild!=NULL)if(child-lchild-rtag==1)child-lchild-rchild=pre;要删除结点有左无右pre-lchild=child-lchild;s=child-lchild;while(s-rchild)s=s-rchild;s-rchild=child-rchild;要删除结点有右无左pre-lchild=child-rchild;s=child-rchild;while(s-lchild)s=s-lchild;s-lchild=child-lchild;if(child-lchild!=NULL)if(child-lchild-rtag==1)child-lchild-rchild=pre;要删除结点左右都有�.pre-lchild=child-lchild;s=child-rchild;while(s-lchild)