《C语言程序设计实验》实验报告样板.docx
《《C语言程序设计实验》实验报告样板.docx》由会员分享,可在线阅读,更多相关《《C语言程序设计实验》实验报告样板.docx(15页珍藏版)》请在冰豆网上搜索。
![《C语言程序设计实验》实验报告样板.docx](https://file1.bdocx.com/fileroot1/2022-12/30/5a05bdb7-b2f1-42a6-8891-767716a7064d/5a05bdb7-b2f1-42a6-8891-767716a7064d1.gif)
《C语言程序设计实验》实验报告样板
《C语言程序设计实验》实验报告(2009-2010第二学期)
所在学院:
专业年级:
学号:
姓名:
指导教师:
2010年7月14日
实习题目:
编制一个演示集合的并、交和差运算的程序
一、需求分析
(1)本演示程序中,集合的元素限定为小写字母字符['a'..'z'],集合的大小n<27。
集合输入的形式为一个以“回车符”为结束标志的字符串,串中的字符顺序不限,且允许出现重复字符或非法字符,程序应能自动滤去。
输出的运算结果字符串中将不含重复字符或非法字符。
(2)演示程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”之后,由用户在键盘上输入演示程序中规定的运算命令;相应的输人数据(滤去输入中的非法字符)和运算结果显示在其后。
(3)程序执行的命令包括:
1)构造集合1;
2)构造集合2;
3)求并集;
4)求交集;
5)求差集;
6)结束。
“构造集合1”和“构造集合2”时,需以字符串的形式键入集合元素。
(4)测试数据
1)Setl='magazine',Set2='paper',
Set1∪Set2='aeglmnprz',Setl∩Set2='ae',Setl-Set2='glmnz‘;
2)Set='012oper4a6hon89',Set2='errordata',
Set1∪Set2='adelnoprt',Set1∩Set='aeort',Setl-Set2='Inp';
二、概要设计
为实现上述程序功能,应以有序链表表示集合。
为此,需要两个抽象数据类型:
有序表和集合。
(1)有序表的抽象数据类型定义为:
ADTOrderedList{
数据对象;D={ai|ai∈CharSet,i=1,2,…,n,n≥0}
数据关系:
R1={<ai-1,ai>|ai-1,ai∈D,ai-1<ai,i=1,2,…,n,}
基本操作:
InitList(&i)
操作结果;构造=个空的有序表L。
DestroyList(&L)
初始条件:
有序表L已存在。
操作结果:
销毁有序表L。
ListLength(L)
初始条件:
有序表L已存在。
操作结果:
返回有序表L的长度。
ListEmpty(L)
初始条件:
有序表L已存在。
操作结果:
若有序表L为空表,则返回True,否则返回False。
GetElem(L,pos)
初始条件:
有序表L已存在。
操作结果:
若l≤pos≤Length(),则返回表中第pos个数据元素。
LocateElem(L,e,&q)
初始条件:
有序表L已存在。
操作结果:
若有序表L中存在元素e,则q指示L中第一个值为e的元素的位置,并返回函数值TRUE;否则e指示第一个大于e的元素的前驱的位置,并返回函数值FALSE。
APPend(&L,e)
初始条件:
有序表L已存在。
操作结果:
在有序表L的末尾插入元素e。
InsertAfter(&L,q,e)
初始条件:
有序表L已存在,q指示L中一个元素。
操作结果:
在有序表L中q指示的元素之后插入元素e。
ListTraverse(q,visit())
初始条件:
有序表L已存在,q指示L中一个元素。
操作结果:
依次对L中q指示的元素开始的每个元素调用过程visit()。
}ADTOrderedList
(2)集合的抽象数据类型定义为:
ADTSet{
数据对象:
①=(ai|ai为小写英文字母且互不相同,i=1,2,…,n,0≤n≤26)
数据关系:
R1={}
基本操作:
Createset(&T,Str)
初始条件:
Str为字符串。
操作结果:
生成一个由*中小写字母构成的集合T。
Destroyset(&T)
初始条件:
集合T已存在。
操作结果:
销毁集合T的结构。
Union(t,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由S1和S2的并集构成的集合T。
Intersection(&T,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由a和S2的交集构成的集合T。
Difference(&T,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由S1和S2的差集构成的集合T。
Printset(T)
初始条件:
集合T已存在。
操作结果:
按字母次序顺序显示集合T的全部元素。
}ADTSet
(3)本程序包含4个模块:
1)主程序模块:
BEGIN{main}
初始化;
REPEAT
接受命令;
处理命令;
UNTIL“命令”=“退出”;
END.
2)集合单元模块——实现集合的抽象数据类型;
3)有序表单元模块——实现有序表的抽象数据类型;
4)结点结构单元模块——定义有序表的结点结构.各模块之间的调用关系如下:
三、详细设计
(1)元素类型、结点类型和指针类型
TYPEElemType—char;{元素类型}
LinkType=^NodeType;{指针类型}
NodeType—RECORD{结点类型}
data:
ElemType;
next:
LlnkType
END;
ViSitProc—PROCEDURE(p:
LinkType);
{访问结点的过程类型}
FUNCMakeNode(VARp:
LinkType;e:
ElemType):
boolean;
{分配由p指向的数据元素为e、后继为“空”的结点,并返回“True”,}
{若分配失败,则返回“False”}
IFMaxAvail<Sizeof(p^)THENreturn(False)
{MaxAvail是标准无参函数,返回堆中最大自由块的大小}
ELSE【new(p);p^.data:
=e;p.next:
=NIL;return(True)】
ENDF;
PROCFreeNode(VARp:
LinkType);
{释放P所指结点}
FUNCCopy(p:
LinkType):
LinkType;
{复制生成和指针P所指结点有同值元素的新结点并返回,若分配空间}
{失败,则返回空指针。
新结点的指针域为NIL}
IFMaxAvailSizeof(p^)THENreturn(NIL)
{MaxAvail是标准无参函数,返回堆中最大自由块的大小}
ELSE【new(s);s.data:
=p”.data;s^.next:
=NIL;return(s)】
ENDF;
FUNCElem(p:
LinkType):
ElemType;
{若指针P<NIL,则返回P所指结点的数据元素,否则返回’#’}
FUNCSuccNode(p:
LinkType):
LinkType;
{若指针P<>NIL,则返回指向P所指结点的后继元素的指针。
}
{否则返回NIL}
(2)根据有序表的基本操作的特点,有序表采用有序链表实现。
链表设头、尾两个指针和表长数据域,并附设头结点,头结点的数据域没有实在意义。
TYPEOrderedList—RECORD{有序链表类型}
head,tail:
LinkType;{分别指向线性链表的头结点和尾结点}
Size:
integer;{指示链表当前的长度}
END;
有序链表的基本操作设置如下:
FUNCInitList(VARL:
OrderedList):
boolean;
{构造一个带头结点的空的有序链表L,并返回“true”;}
{若分配空间失败则令L.head为NIL,并返回“False”}
PROCDestroyList(VARL:
OrderedList);
{销毁有序链表L}
FUNCListEmpty(L:
OrderedList):
boollean;
{若L不存在或为“空表”,则返回“True”,否则返回“False”}
FUNCListLength(L:
OrderedList):
Integer;
{返回链表的长度}
FUNCGetElemPos(L:
OrderedList;Pos:
integer):
LinkType;
{若L存在且O<Pos<L.Size+1,则返回指向第pos个元素的指针,}
{否则返回NIL}
FUNCLOCateElem(L:
OrderedList;e:
ElemType;
VARq:
LinkType):
boolean;
{若有序链表L存在且表中存在元素e,则q指示L中第一个值为e的结点的位置,并返回True;否则q指示第一个大于e的元素的前驱的位置,并返回False}
PROCAppend(VARL:
OrderedList;s:
LinkType);
{在已存在的有序链表L的末尾插入指针S所指结点}
PROCInsertAfter(VARL:
OrderList;q:
LinkType;s:
linkType);
{在已存在的有序链表L中q所指示的结点之后插入指针S所指结点}
PROCTraverseList(p:
LinkType;viS1t:
Proc);
{从p(p<>NIL)指示的结点开始,依次对每个结点调用过程Visit()}
其中部分操作的伪码算法如下:
FUNCInitList(VARL:
OrderedList):
boolean;
IFMakeNode(head,'')THEN{头结点的虚设元素为空格符''}
【L.tail:
=L.head;L.Size:
=0;
return(True)
】
ELSE【
L.head:
=NIL;
return(False)
】
ENDP;{InitList}
PROCDestroyList(VARL:
OrderedList);
WITHLDO
【p:
=head;
WHILEp<>NILDO
【q:
=PP:
=SuCCNode(P);FreeNode(q)】;
head:
=tail:
=NIL
】
ENDP;{DestroyList}
FUNCGetElemPos(L:
OrderedList;pos:
integer)):
LinkType;
IF(L.head=NIL)OR(pos<1)OR(pos>L.Size)THENreturn(NIL)
ELSEIFpos=L.SizeTHENreturn(L.tail)
ELSE【
p:
=L.head^.next;k:
=1;
WHILE(p<>NIL)CAND(k<Pos)DO
【p:
=SuccNode(p);k:
=k+1;】
return(P)
】
ENDF;{GetElemPos}
FUNCLocateElem(L:
OrderedList;e:
ElemType;
VARp:
LinkType):
boolean;
IFL.head<>NILTHEN【
pre:
=L.head;p:
=pre.next;
{pre指向p^的前驱,P指向第一个元素结点}
WHILE(p<>NIL)CAND(p^.data<e)DO
【Pre:
=PP:
=SuccNode(p)】;
IF(p<>NIL)CAND(p^.data=e)THENreturn(True)
ELSE【p:
=pre;return(False)】
】
ELSEreturn(False)
ENDF;{LocateElem}
PROCAppend(VARL:
OrderedList;s:
LinkType);
IF(L.head<>NIL)CAND(s<>NIL)THEN【
WITHLDO
IFtail<>headTHEN【tail^.next:
=s;tail:
=s】
ELSE【head^.next:
=s;tail:
=s】;
Inc(L.Size)】
ENDP;{Append}
PROCInsertAfter(VARL:
OrderListt;q:
LinkType;s:
LinkType);
IF(L.head<>NIL)AND(q<>NIL)CAND(s<>NIL)THEN
【s.next:
=q^.next;q^.next:
=s;
IFL.tail=qTHENL.tail:
=s;
Inc(L.Size)
】
ENDP;{InsertAfter}
PROCListTraverse(p:
LinkTypeviS1t:
Proc);
WHILEp<>NILDO
【visit(p);p:
=SuccNOde(p)】
ENDP;{ListTraverse}
(3)集合Set利用有序链表类型OrderedLList来实现,定义为有序集Orderedset:
TYPEOrderedset—OrderedList;
集合类型的基本操作的类Pascal伪码描述如下:
PROCCreateset(VART:
Orderedset;s:
string);
{生成由串s中小写字母构成的集合T,IsLower是小写字母判别函数}
IFInitList(T){构造空集T}THEN
FORi:
=1TOlength(s)DO
IFIsLower(s[i])CANDNOTLocateElem(T,s[i],p)THEN
{过滤重复元素并按字母次序大小插入}
IFMakeNode(q,s[i])THEN
InsertAfter(T,p,q)
ENDP;{Createset}
PROCDestroyset(VART:
Orderedset);
{销毁集合T的结构}
DestroyList(T)
ENDP;{DestroyList}
PROCUnion(VART:
Orderedset;S1,S2:
Orderedset);
{求已建成的集合S1和S2的并集T,即S1.head<>NIL且S2.head<>NIL}
IFInitList(T)THEN【
pl:
=GetElemPos(s1,1);p2:
=GetElemPos(S2,1);
WHILE(pl<>NIL)AND(p2<>NIL)DO
【c1:
=Elem(pl);c2=Elem(p2);
IFC1<=C2THEN
【Append(T,Copy(pl));pl:
=SuCCNOde(pl);
IFC1=c2THENp2:
=SUCCNOde(p2);
】
ELSE【Append(T,Copy(p2));p2:
=SuccNode(p2);】
】
WHILEpl<>NILDO
【Append(T,Copy(pl));pl:
=SuccNode(pl);】;
WHILEp2<>NILDO
【Append(T,Copy(p2));p2:
=SuccNode(p2);】
ENDP;{Union}
PROCEDUREIntersection(VART:
Orderedset;S1,S2:
Orderedset);
{求集合S1和S2的交集T}
IFNOTInitList(T)THENT.head=NIL
ELSE【
pl:
=GetElemPOs(S1,1);p2:
=GetElemPos(S2,1);
WHILE(pl<>NIL)AND(p2<>NIL)DO
【c1:
=Elem(pl);c2:
=Elem(p2);
IFC1<C2THENpl:
=SuccNode(pl)
ELSEIFC1>C2THENp2:
=SuccNode(p2)
ELSE{c1=c2}
【Append(T,Copy(pl));pl:
=SuccNode(pl);
p2:
=SuccNOde(p2)】
ENDP;{Intersection}
PROCDifference(VART:
Orderedset;S1,S2:
Orderedset);
{求集合S1和S2的差集T}
IFNOTInitList(T)THENT.head:
=NIL
ELSE【
pl:
=GetElemPos(S1,1);p2:
=GetElemPos(S2,1);
WHILE(p1<>NIL)AND(p2<>NIL)DO
【c1:
=Elem(p1);c2=Elem(p2);
IFc1<c2THEN
【Append(T,Copy(pl));pl:
=SuccNode(pl)】
ELSEIFC1>C2THENp2:
=SuccNode(p2)
ELSE{c1=C2}
【pl:
=SuccNode(pl);p2:
=SuccNode(p2)】
】;
WHILEp1<>NILDO
【Append(T,Copy(pl));pl:
=SuccNode(pl)】
】
ENDP;{Difference}
PROCWritesetElem(p:
LlnkType);
{显示集合的一个元素}
write(',');WriteElem(Elem(p));
ENDP;{WritesetElem}
PROCPrintset(T:
Orderedset);
{按Pascal格式显示集合的全部元素}
p:
=GetElemPos(T,1);
write('[');
IFp<>NILTHEN
【writeSetElem(p);p:
=SuccNode(p)】
ListTraverse(p,WrltesetElem);
wrietelnn(']');
ENDP;{Printset}
(4)主程序和其他过程的伪码算法
BEGIN{主程序}
Initialization;(初始化)
REPEAT
ReadCommand(cmd);{读入一个操作命令符}
InterPret(cmd){解释执行操作命令符}
UNTILcmdIN['q','Q']
END.{main}
PROCInitialization;{系统初始化}
Clrscr;{清屏}
在屏幕上方显示操作命令清单:
Makesetl--1
Makeset2--2
Union--u
Intersaction--i
Difference--d
Quit--q;
在屏幕下方显示操作命令提示框;
Createset(Setl,'');Printset(Setl);
{构造并显示空集Setl}
Createset(Set2,'');Printset(Setl)
{构造并显示空集Set2}
ENDP;{Initializatlon}
PROCReadCommand(VARcmd:
char);
{读入操作命令符}
显示键入操作命令符的提示信息;
REPEAT
cmd:
=ReadKey
UNTILcmdIN['1','2','u','U','i','I','d','D','q','Q'];
回显cmd
ENDP;
PROCInterpret(cmd:
Char);
{解释执行操作命令cmd}
CASEcmdOF
'1':
【显示以串的形式键人集合元素的提示信息;
readh(v);{读入集合元素到串变量v}
Createset(Setl,v);Printset(Setl);{构造并显示有序集Setl}
】;
'2':
【显示以串的形式键入集合元素的提示信息;
rcaredln(v);{读入集合元素到串变量v}
Createset(Set2,v);Printset(Set2);{构造并显示有序集Set2}
】;
'u','U':
【Union(Set3,Setl,Set2);
{求有序集Setl和Set2的并集Set3}
Printset(Set3);{显示并集Set3}
DestroyList(Set3){销毁并集Set3}
】;
'i','I':
【Intersaction(Set3,Setl,Set2);
{求有序集Setl和Set2的交集Set3}
Printset(Set3);
DestroyList(Set3)
】;
'd‘,'D':
【Difference(Set3,Setl,Set2);
{求集合Setl和Set2的差集Set3}
Printset(Set3);
DestroyList(Set3)
】;
ENDC;
ENDP;(Interpret)
(5)过程和函数的调用关系图反映了演示程序的层次结构:
四、调试分析
(1)由于对集合的3种运算的算法推敲不足,在有序链表类型的早期版本未设置尾指针和APPend操作,导致算法低效。
(2)初时曾忽略了一些变量参数的标识“VAR”,使调试程序时费时不少。
今后应重视确定参数的变量和赋值属性的区分和标识。
(3)本程序的模块划分比较合理,且尽可能将指针的操作封装在结点和链表的两个模块中,致使集合模块的调试比较顺利。
反之,如此划分的模块并非完全合理,因为在实现集合操作的编码中仍然需要判别指针是否为空,按理,两个链表的并、交和差的操作也应封装在链表的模块中,而在集合的模块中,只要进行相应的应用即可。
(4)算法的时空分析
1)由于有序表采用带头结点的有序单链表,并增设尾指针和表的长度两个标识,各种操作的算法时间复杂度比较合理。
InitList、ListEmpty、Listlength、Append和InsertAfter以及确定链表中第一个结点和之后一个结点的位置都是O
(1)的,DestroyList、LocateElem和TraverseList及确定链表中间结点的位置等则是O(n)的,n为链表长度。
2)基于有序链表实现的有序集的各种运算和操作的时间复杂度分析如下:
构造有序集算法Createset读入n个元素,逐个用LocateElem判定不在当前集合中及确定插入位置后,才用InsertAfter插入到有序集中,所以时间复杂度是O(n)。
求并集算法Union利用集合的“有序性”将两个集合的m+n个元素不重复地依次利用Append插入到当前并集的末尾,故可在O(m+n)时间内完成。
可对求交集算法Intersection和求差集算法Difference作类似地分析,它们也是O(m+n)的。
销毁集合算法Destroyset和显示集合算法Printset都是对每个元素调用一个O
(1)的过程,因此都是O(n)的。
除了构造有序集算法Createset用一个串变量读入n个元素,需要O(n)的辅助空间外,其余算法使用的辅助空间与元素个数无关,即是O
(1)的。
(5)本实习作业采用数据抽象的程序设计方法,将程序划分为四个层次结构:
元素结点、有序链表、有序集和主控模块,使得设计时思路清晰,实现时调试顺利,各模块具有较好的可重用性,确实得到了一次良好的程序设计训练。
五、用户手册
(1)本程序的运行环境为DOS操作系统,执行文件为:
SetDemos.exe。
(2)进