《计算机算法基础》.docx

上传人:b****6 文档编号:8820426 上传时间:2023-02-01 格式:DOCX 页数:23 大小:98.72KB
下载 相关 举报
《计算机算法基础》.docx_第1页
第1页 / 共23页
《计算机算法基础》.docx_第2页
第2页 / 共23页
《计算机算法基础》.docx_第3页
第3页 / 共23页
《计算机算法基础》.docx_第4页
第4页 / 共23页
《计算机算法基础》.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

《计算机算法基础》.docx

《《计算机算法基础》.docx》由会员分享,可在线阅读,更多相关《《计算机算法基础》.docx(23页珍藏版)》请在冰豆网上搜索。

《计算机算法基础》.docx

《计算机算法基础》

 

《计算机算法基础》

课程设计报告

标题:

最优二分检索树

单位:

计算机学院2001级13班

报告人:

龚勋200131500342

指导教师:

袁梦霆

编程环境:

Turboc2.0

时间:

2003年12月20日

 

1.设计要求

设计一程序,能够读入多个C源程序,识别其中的标示符(包括关键字和非关键字),从而获得关键字和非关键字出现的概率P、Q,根据P、Q生成一棵最优二分检索树,在屏幕上显示这棵树,分别用二分检索和该最优二分检索树对某一个C语言程序中的标示符进行检索,并统计检索成本(即比较次数)。

2.分析

由设计要求可确定程序的几大模块:

读入源程序,找出其中的标示符对P、Q计数构造二分检索树显示该二分检索数用该二分检索树和二分检索同一C程序中的标示符,比较检索成本。

进一步确定几个子程序及相互间的调用关系为:

主函数调用读文件子程序,由后者依次分析指定源程序中的每个字符,若找到一个标示符,调用计数子程序对P或Q中的某一对应项进行加一操作,返回读文件子程序继续分析并调用计数子程序对P、Q计数,直至指定源程序结束,若还需继续读入,再调用度文件子程序对下一个指定的源程序进行读入和对P、Q进行计数操作,直到用户不再需要读入源程序为止。

然后利用动态规划的思想由P、Q计算每一棵可能二分检索树子树的最小检索成本和对应的根结点R,下一步即可由生成的R构造出一棵最优二分检索树。

接下来画出这棵二叉树,再用这棵二分检索树和二分检索法方法去检索同一C程序,进行检索成本的比较,至此,整个程序结束。

3.流程图(见下页)

4.全局变量与子程序

(1)全局变量

char*key[]:

存放C语言32个关键字的指针数组;

intfind1,find2:

分别用来记录用户输入的C源程序名和最后的检索比较程序名是否合法;

intp[33],q[33]:

p[i]和q[i]分别是成功检索和不成功检索的概率;

intw[33][33],c[33][33],r[33][33]:

w[i][j]为子树T(i,j)的概率;c[i][j]为子树T(i,j)的检索成本;r[i][j]为子树T(i,j)的根结点序号;

structtree

{intnumber;

structtree*llink,*rlink;

}:

内存中结点的表示方式;

structtree*root:

内存中二叉树的根结点;

intquantity=0,times1=0,times2=0:

作检索成本比较时文件中标识符的个数和两种方法各自比较次数*/

(2)count()子程序

形参:

字符数组buffer1

返回参数:

功能:

用二分检索查找存放在buffer1中的字符串,若找到,对P中该关键字的计数值加一;否则,对Q中相应位加一;

调用情况:

由reading()子程序调用后返回;

(3)binary()子程序

形参:

字符数组buffer2;

返回参数:

无;

功能:

对存放在buffer2中的字符串进行二分检索,统计比较次数;

调用情况:

由reading()子程序调用后返回;

(4)binarytree()子程序

形参:

字符数组buffer3;

返回参数:

无;

功能:

利用构造的二分检索树对buffer3中的字符串进行检索,统计比较次数;

调用情况:

由reading()子程序调用后返回;

(5)save()子程序

形参:

无;

返回参数:

无;

功能:

统计所有源文件中标识符的个数并将P、Q存入指定文件中;

调用情况:

由reading()子程序调用后返回;

(6)reading()子程序

形参:

整形变量command,当command=1,表明是建树时调用;当command=2,表明是比较检索成本时调用;

返回参数:

无;

功能:

当command=1,读入用户指定的源文件,按字符依次搜索其中的标识符,每找到一个便调用count()子程序对P、Q计数,直至文件结束,若还需读文件,则继续调用其本身,否则调用save()子程序将P、Q存入指定文件后,然后返回主程序;

调用情况:

由主程序调用;

(7)OBST()子程序

形参:

无;

返回参数:

无;

功能:

利用动态规划的思想计算所有可能子树的根结点序号;

调用情况:

由主程序调用;

(8)createtree()子程序

形参:

整形变量a、b,分别是待建二叉树结点对应序号的起止值;

返回参数:

最优二分检索树的根结点;

功能:

在内存中构造最优二分检索树;

调用情况:

由主程序调用;

(9)high()子程序

形参:

指向根结点的结构体指针;

返回参数:

最优二分检索树的高度;

功能:

求构造的最优二分检索树的高度;

调用情况:

由绘图子程序drawtree()调用;

(10)drawtree()子程序

形参:

检索树的高度和内存中的根结点指针;

返回参数:

无;

功能:

在屏幕上画出最优二分检索树;

调用情况:

由主程序调用;

5.源程序

#include

#include

#include

#include

#defineLENsizeof(structtree)

char*key[]={"","auto","break","case","char","const","continue","default","do","double",

"else","enum","extern","float","for","goto","if","int","long","register",

"return","short","signed","sizeof","static","struct","switch","typedef",

"union","unsigned","void","volatile","while"};/*C语言所有关键字,共32个*/

intfind1=1,find2=1;/*判断输入的文件名是否有效*/

intp[33],q[33];/*统计源文件中关键字和非关键字出现的概率*/

intw[33][33],c[33][33],r[33][33];/*为构造二分检索树定义的数组*/

structtree

{intnumber;

structtree*llink,*rlink;

};/*二分检索树结点的内存中的表示方式*/

structtree*root;/*构造的检索树的根结点*/

intquantity=0,times1=0,times2=0;/*作检索成本比较时用来记录标识符的个数和总共比较次数*/

voidcount(charbuffer1[])

{/*统计一C程序中的各关键字和非关键字的个数*/

intlow=1,high=32,mid;

while(low<=high)

{mid=(low+high)/2;

if(strcmp(buffer1,key[mid])<0)

high=mid-1;

elseif(strcmp(buffer1,key[mid])>0)

low=mid+1;

else

{p[mid]++;

return;/*能检索到,则相应关键字的计数位加1并返回到reading()函数*/

}

}

q[(low+high)/2]++;/*检索不到,则该非关键字的位置应在两个数组指针正中间*/

return;

}

voidbinary(charbuffer2[])

{/*统计待检索的C程序的标识符个数和二分检索的比较次数*/

intlow=1,high=32,mid;

quantity++;

while(low<=high)

{times1++;/*每判断大小一次,比较次数加1*/

mid=(low+high)/2;

if(strcmp(buffer2,key[mid])<0)

high=mid-1;

elseif(strcmp(buffer2,key[mid])>0)

low=mid+1;

else

return;

}

}

voidbinarytree(charbuffer3[])

{/*统计采用构造的检索树的检索成本*/

structtree*p=root;

while(p)

{times2++;

if(strcmp(buffer3,key[p->number])<0)

p=p->llink;

elseif(strcmp(buffer3,key[p->number])>0)

p=p->rlink;

else

return;

}

}

voidsave()

{/*将关键字和非关键字的计数值P、Q存入指定文件中*/

charfilename[20];

FILE*fp;

inti,numbers=0;

printf("\nthefileyouwanttosavethearraysP,Q:

");

scanf("%s",filename);

if((fp=fopen(filename,"w"))==NULL)

{printf("\nfilenotfind,sothearraysPandQcannotsave");

return;/*指定存入的文件名错误,则返回*/

}

printf("savesuccess!

");

fputc('P',fp);

fputc(':

',fp);

fputc('',fp);

for(i=1;i<=32;i++)

fprintf(fp,"%2d,",p[i]);

fputc('\n',fp);

fputc('Q',fp);

fputc(':

',fp);

for(i=0;i<=32;i++)

fprintf(fp,"%2d,",q[i]);

numbers+=q[0];

for(i=1;i<=32;i++)

{numbers+=p[i];

numbers+=q[i];

}

printf("\nthereare%didentifiers",numbers);

}

voidreading(intcommand)

{/*依次按字符读C源文件,每遇到合法标识符转count()函数修改计数值,

command变量用来区别是建树时还是比较检索成本时调用的该函数*/

FILE*fp;

charfileName[20];

charch;

charbuffer[35];

intstatus=0;/*定义的状态变量*/

inti=0,j;

charchoose;

printf("\nthefilename:

");

scanf("%s",fileName);

if((fp=fopen(fileName,"r"))==NULL)

{printf("cannotopenfile");

if(command==1)

find1=0;

if(command==2)

find2=0;

return;

}

getchar();/*吸收输入文件名时最后敲入的回车*/

ch=fgetc(fp);

while(ch!

=EOF)/*按字符依次读文件,直至结束*/

{switch(status)

{case0:

if(((ch>='A')&&(ch<='Z'))||((ch>='a')&&(ch<='z'))||(ch=='_'))

/*在初态下,遇到字母或下划线,则是合法标识符的开始*/

{status=4;

buffer[i]=ch;/*转状态4并将读入字符存入缓冲数组*/

i++;

}

elseif(ch=='/')/*若是'/'字符,有可能是合法注释的开始,转状态1*/

status=1;

break;

case1:

if(ch=='*')/*在已经遇到'/'字符的情况下再检索到'*'字符,

则是合法注释的开始*/

status=2;

else/*若'/'字符之后不是'*',则转回初态*/

status=0;

break;

case2:

if(ch=='*')/*遇到第二个'*'字符,有可能是注释的结束,转状态3*/

status=3;

break;

case3:

if(ch=='/')/*合法注释的结束*/

status=0;

else

status=2;/*否则,刚才遇到的'*'字符无效,转回状态2*/

break;

case4:

if(((ch>='A')&&(ch<='Z'))||((ch>='a')&&(ch<='z'))||(ch=='_')||

((ch>='0')&&(ch<='9')))

/*在已经遇到字母或下划线之后,又检索到字母、下划线或

数字,被认为是合法标识符的组成,存入缓冲数组,状态不变*/

{buffer[i]=ch;

i++;

}

else/*遇到合法标示符组成以外的字符,则认为是

标识符结束,缓冲数组里存放的是一个合法标识符*/

{status=0;

buffer[i]='\0';/*转回到初态并对缓冲字符串加结束符*/

i=0;

if(command==1)

count(buffer);/*若是建树时调用此函数,则转count()函数

对P、Q计数*/

if(command==2)

{binary(buffer);

binarytree(buffer);

}/*若是比较检索成本时调用,分别转统计比较次数的函数*/

for(j=0;j<=i;j++)

buffer[j]='\0';/*别忘了清空缓冲区*/

}

}

ch=fgetc(fp);

}

if(command==1)

{printf("\ncontinueornot?

y(Yes)orn(Not):

");

scanf("%c",&choose);

getchar();

if(choose=='y')

reading

(1);/*还需统计,则继续读源文件*/

else

save();/*不需继续统计,转save()函数将P、Q存入指定文件*/

}

}

voidOBST()

{/*计算每一子树的根结点的序号,以用来构造最优二分检索树*/

inti,m,j,k,l;

intmin=32767;

for(i=0;i<=31;i++)

{w[i][i]=q[i];

r[i][i]=0;

c[i][i]=0;/*不含关键字的子树*/

w[i][i+1]=q[i]+q[i+1]+p[i+1];

r[i][i+1]=i+1;

c[i][i+1]=q[i]+q[i+1]+p[i+1];/*含一个关键字的子树*/

}

w[32][32]=q[32];

r[32][32]=0;

c[32][32]=0;

for(m=2;m<=32;m++)/*含m个关键字的结点*/

for(i=0;i<=32-m;i++)

{j=i+m;

w[i][j]=w[i][j-1]+p[j]+q[j];

for(l=r[i][j-1];l<=r[i+1][j];l++)

{if(c[i][l-1]+c[l][j]

{k=l;

min=c[i][l-1]+c[l][j];

}

}/*注意'l'和'1'!

*/

c[i][j]=w[i][j]+c[i][k-1]+c[k][j];

r[i][j]=k;

min=32767;

}

}

structtree*createtree(inta,intb)

{/*建立最优二分检索树*/

structtree*pointer,*p,*q;

if(a

{pointer=(structtree*)malloc(LEN);

pointer->number=r[a][b];/*创建根结点*/

pointer->llink=NULL;

pointer->rlink=NULL;

if(a

{p=createtree(a,r[a][b]-1);

pointer->llink=p;

}

if(r[a][b]

{q=createtree(r[a][b],b);

pointer->rlink=q;

}

}

returnpointer;/*将构建的检索树的根结点指针赋予全局变量root*/

}

inthigh(structtree*r)

{/*求最优二分检索树的高度*/

inta,b,h;

if(r!

=NULL)

{if(r->llink!

=NULL)

a=high(r->llink);/*递归求左子树的高度*/

else

a=0;

if(r->rlink!

=NULL)/*递归求右子树的高度*/

b=high(r->rlink);

else

b=0;

h=1+((a>b)?

a:

b);

}

else

h=0;

returnh;

}

voiddrawtree(inthigh,structtree*roots)

{/*绘图函数*/

structtree*p;

inth=(480-8*high)/(high-1);/*求连线的高度*/

intx=0,y=0,min=32767,max=-32768;

floattemp1=0,temp2=0,temp3,temp4,temp;

inti,aa,bb;

intwidth[33]={0,80,70,60,50,40,30,20,10,6,4,2};/*各级结点间连线的宽度*/

intgraphdriver=DETECT;

intgraphmode;

chars[10];

struct/*将绘图时32个结点的信息存入一个结构队列中*/

{intvalue;/*序号*/

intx_coor;/*x坐标*/

inty_coor;/*y坐标*/

structtree*q;/*内存中指向该结点的指针*/

intdepth;/*高度(根结点为1)*/

intleft_right;/*左或右孩子(左孩子为1,右孩子为0),在实际绘图中用不到

该数据项,只作为一个标识显示给用户*/

}pointers[50];

intf,r;/*队首和队尾指针*/

struct/*将31条连线的信息存入另一个结构数组中*/

{intx1;/*起点的x坐标*/

inty1;/*起点的y坐标*/

intx2;/*终点的x坐标*/

inty2;/*终点的y坐标*/

}lines[50];

inttop=0;

for(i=12;i<=32;i++)

width[i]=1;

p=roots;

while(p->llink!

=NULL)/*下面一段程序利用1号结点和32号结点所在的高度对

图画中根结点进行定位,以使左右子树都能完整显示*/

{x++;

p=p->llink;

}/*求1号结点(最左端结点)的高度*/

p=roots;

while(p->rlink!

=NULL)

{y++;

p=p->rlink;

}/*求32号结点(最右端结点)的高度*/

p=roots;

for(i=1;i<=x;i++)

temp1+=width[i];/*求1号结点相对于根结点在x方向上的偏移*/

for(i=0;i<=y;i++)

temp2+=width[i];/*求32号结点相对于根结点在x方向上的偏移*/

temp=temp1/(temp1+temp2);/*求1号结点相对于根结点的偏移占整个屏幕宽度的百分比*/

printf("\nx=%d,y=%d,temp1=%f,temp2=%f,temp=%f,x0=%f",x,y,temp1,temp2,temp,640*temp);

f=1;

r=1;

/*将根结点信息存入结点队列,考虑到显示关键字需占较多像素,故用其在关键字序列中的序号代替*/

pointers[f].value=roots->number;

pointers[f].x_coor=640*temp-((pointers[f].value<10)?

4:

8);

pointers[f].y_coor=0;

pointers[f].q=p;

pointers[f].depth=1;

pointers[f].left_right=1;/*将根结点定义为左结点*/

/*对各结点按层次遍历二叉树的顺序进行定位,对结点队列中的结点依次进行如下操作:

确定它与左右孩子间连线的起止点坐标;由这些坐标定位它的左右孩子*/

while(f<=32)

{if(pointers[f].q->llink!

=NULL)

{/*若有左子树*/

top++;

lines[top].x1=pointers[f].x_coor+((pointers[f].value<10)?

4:

8);

lines[top].y1=pointers[f].y_coor+8;

lines[top].x2=pointers[f].x_coor+((pointers[f].value<10)?

4:

8)-width[pointers[f].depth];

lines[top].y2=pointers[f].y_coor+8+h;

aa=(pointers[r].value<10)?

8:

16;

bb=(pointers[f].q->llink->number<10)?

4:

8;

if((pointers[r].y_coor==lines[top].y2)&&(pointers[r].x_coor+aa>=lines[top].x2-bb))

{/*考虑连线的交叉和结点的重叠问题,若有,让两条线各向两边退让一些像素*/

temp3=lines[top].x2;

temp4=lines[top-1].x2;

lines[top].x2+=((a

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

当前位置:首页 > 高等教育 > 农学

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

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