基本操作:
InitList(&L)
操作结果;构造一个空的有序表L。
DestroyList(&L)
初始条件:
有序表L已存在。
操作结果:
销毁有序表L。
ListLength(L)
初始条件:
有序表L已存在。
操作结果:
返回有序表L的长度。
ListEmpty(L)
初始条件:
有序表L已存在。
操作结果:
若有序表L为空表,返回True,否则返回False。
GetElem(L,i,&e)
初始条件:
有序表L已存在,若1<=i<=Length(L)。
操作结果:
用e返回L中第i个元素的值。
LocateElem(L,e,compare())
初始条件:
有序表L已存在,compare()是数据元素判定函数
操作结果:
返回L中第一个与e满足关系compare()的数据元素的位序。
若这样的元素不存在,则返回值为0。
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()。
}ADTOderedList
2.集合的抽象数据类型定义:
ADTSet{
数据对象:
D={ai|ai为小写英文字母且互不相同,i=1,2,....,n,0<=n<=26}
数据关系:
R1={}
基本操作:
CreateSet(&T,Str)
初始条件:
Str为字符串。
操作结果:
生成一个由Str中小写字母构成的集合T。
DestroySet(&T)
初始条件:
集合T已存在。
操作结果:
销毁集合T的结构。
Union(&T,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由S1和S2的并集构成的集合T。
Intersection(&T,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由S1和S2的并集构成的集合T。
Difference(&T,S1,S2)
初始条件:
集合S1和S2存在。
操作结果:
生成一个由S1和S2的差集构成的集合T。
PrintSet(T)
初始条件:
集合T已存在。
操作结果:
按字母次序顺序显示集合T的全部元素。
}ADTSet
3.本程序包含四个模块
(1)主程序模块:
voidmain(){
初始化;
do{
接受命令;
处理命令;
}while(“命令”=“退出”);
}
(2)集合单元模块—实现集合的抽象数据类型;
(3)有序表单元模块—实现有序表的抽象数据类型;
(4)结点结构单元模块—定义有序表的结点结构。
各模块调用关系:
主程序模块→集合单元模块→有序表单元模块→结点结构单元模块
三、详细设计
a)函数的调用关系图为:
b)具体函数代码为:
#defineTRUE1
#defineFALSE0
#defineOK1
#defineERROR0
#defineINFEASIBLE-1
#defineOVERFLOW-2
//函数常量的声明//
typedefintStatus;
typedefcharElemType;
//元素类型的定义//
#include
#include
#include
#include
//函数的头文件//
ElemTypea[100]="magazine";
ElemTypeb[100]="paper";
OrderedListL1,L2,L3;//定义三个线性链表//
//定义全局变量//
voidmain()
{
charcmd;
do
{
ReadCommand(cmd);//读出命令//
Interpret(cmd);//执行命令//
}while(cmd!
='q');//当输入的命令为”q”时,退出程序//
}
//该实验函数的主函数//
voidReadCommand(char&cmd)
{
printf("\n****************************************************************************\n");
printf("MakeSet1-1\tMakeSet2-2\tUnion-u\t\nIntersection-i\tDifference-d\tQuit-q\t\tDisplay-y");
printf("\n****************************************************************************\n");
printf("\n\n请选择操作:
");
cmd=getch();
if(cmd!
='1'&&cmd!
='2'&&cmd!
='u'&&cmd!
='i'&&cmd!
='d'&&cmd!
='q'&&cmd!
='y')
{
printf("\n你输入的是非法指令,请重新输入!
\n");
ReadCommand(cmd);
}
}//编写一个用户使用手册并输入命令//
voidInterpret(char&cmd)
{
system("cls");//清屏//
switch(cmd)
{
case'1':
printf("\n\t请输入字符串:
");
gets(a);
printf("\t原始字符串:
");
printf("\t%s\n",a);
CreateSet(L1,a);
printf("\t构建的集合S1为:
");
ListTraverse(L1.head->next,Print);
break;//当输入的指令为”1”时,利用输入的元素建立集合S1//
case'2':
printf("\n\t请输入字符串:
");
gets(b);
printf("\t原始字符串:
");
printf("\t%s\n",b);
CreateSet(L2,b);
printf("\t构建的集合S2:
");
ListTraverse(L2.head->next,Print);
break;//当输入的指令为”2”时,利用输入的元素建立集合S2//
case'u':
CreateSet(L1,a);
CreateSet(L2,b);
Union(L3,L1,L2);
printf("\n\t集合1:
");
ListTraverse(L1.head->next,Print);
printf("\t集合2:
");
ListTraverse(L2.head->next,Print);
printf("\t并集:
");
ListTraverse(L3.head->next,Print);
break;//当输入的指令为”u”时,执行集合的并集运算并输出//
case'i':
CreateSet(L1,a);
CreateSet(L2,b);
Intersection(L3,L1,L2);
printf("\n\t集合1:
");
ListTraverse(L1.head->next,Print);
printf("\t集合2:
");
ListTraverse(L2.head->next,Print);
printf("\t交集:
");
ListTraverse(L3.head->next,Print);
break;//当输入的指令为”i”时,执行集合的交集运算并输出//
case'd':
CreateSet(L1,a);
CreateSet(L2,b);
Difference(L3,L1,L2);
printf("\n\t集合1:
");
ListTraverse(L1.head->next,Print);
printf("\t集合2:
");
ListTraverse(L2.head->next,Print);
printf("\t差集:
");
ListTraverse(L3.head->next,Print);
break;//当输入的指令为”d”时,执行集合的差集运算并输出//
case'y':
printf("\n\t原始字符串:
\n");
printf("\t\t%s\n\t\t%s\n",a,b);
CreateSet(L1,a);
CreateSet(L2,b);
printf("\t由字符串构建的集合:
\n");
printf("\t");
ListTraverse(L1.head->next,Print);
printf("\t");
ListTraverse(L2.head->next,Print);
Union(L3,L1,L2);
printf("\t并集:
");
ListTraverse(L3.head->next,Print);
Intersection(L3,L1,L2);
printf("\t交集:
");
ListTraverse(L3.head->next,Print);
Difference(L3,L1,L2);
printf("\t差集:
");
ListTraverse(L3.head->next,Print);
break;//当输入的指令为”2”时,同时进行集合的交并差集运算并输出//
}
}
//读取相关指令进行运算并输出结果//
voidCreateSet(OrderedList&T,char*s)
{
unsignedi;
LinkTypep,q;
if(InitList(T))
{
for(i=0;i<=strlen(s);i++)
{
if(s[i]>='a'&&s[i]<='z'&&!
LocateElem(T,s[i],p))
{
if(MakeNode(q,s[i]))
{
InsertAfter(T,p,q);//当输入的是合法字符时,创建包含该元素的结点
}
}
}
}
}
//创建一个能存储有用户输入的元素的线性链表//
voidInsertAfter(OrderedListL,LinkTypeq,LinkTypes)
{
if(L.head&&q&&s)
{
s->next=q->next;
q->next=s;
if(L.tail==q)
L.tail=s;
L.size++;
}
}
//在链表的指定中插入一个结点//
StatusLocateElem(OrderedListL,ElemTypee,LinkType&p)
{
NodeType*pre;
if(L.head)
{
pre=L.head;
p=pre->next;
while(p&&p->data{
pre=p;
p=p->next;
}
if(p&&p->data==e)
{
returnTRUE;
}
else
{
p=pre;
returnFALSE;
}
}
else
returnFALSE;
}
//建立一个元素从小到大排列的链表并返回TRUE,否则返回FALSE//
StatusInitList(OrderedList&L)
{
if(MakeNode(L.head,''))
{
L.tail=L.head;
L.size=0;
returnTRUE;
}
else
{
L.head=NULL;
returnFALSE;
}
}
//创建一个有头结点的线性链表并返回TRUE,否则返回FALSE//
voidListTraverse(LinkTypep,Status(*visit)(LinkType))
{
printf("%c",'\t');
while(p)
{
visit(p);
p=p->next;
}
printf("%c",'\n');
}
//将用户输入的元素转化为集合并输出//
StatusMakeNode(LinkType&p,ElemTypee)
{
p=(LinkType)malloc(sizeof(NodeType));//为建立的结点创建一个内存空间//
if(!
p)
returnFALSE;
p->data=e;
p->next=NULL;
returnTRUE;
}
//创建一个结点并返回TRUE,否则返回FALSE//
typedefstructNodeType
{
ElemTypedata;
structNodeType*next;
}NodeType,*LinkType;
//定义一个线性链表的指针结构类型//
typedefstruct
{
LinkTypehead,tail;//定义线性表的头指针和尾指针//
intsize;//表示该链表的长度//
}OrderedList;
voidUnion(OrderedList&T,OrderedListS1,OrderedListS2)
{
LinkTypep1,p2,p;
if(InitList(T))
{
p1=S1.head->next;
p2=S2.head->next;
while(p1&&p2)
{
if(p1->data<=p2->data)
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p1->data;
p->next=NULL;
Append(T,p);
if(p1->data==p2->data)
p2=p2->next;
p1=p1->next;
}
else
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p2->data;
p->next=NULL;
Append(T,p);
p2=p2->next;
}
}//将两个集合的元素比较按照从小到大的顺序输入到新的集合中//
while(p1)
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p1->data;
p->next=NULL;
Append(T,p);
p1=p1->next;
}
while(p2)
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p2->data;
p->next=NULL;
Append(T,p);
p2=p2->next;
}
}//比较完后将两个集合中元素较多的那个集合中剩余的元素全部插入到新建的集合中
}
//求集合S1和集合S2中元素的并集//
voidAppend(OrderedList&L,LinkTypes)
{
if(L.head&&s)
{
if(L.tail!
=L.head)
else
L.tail->next=s;
L.head->next=s;
L.tail=s;
L.size++;
}
}
//插入一个结点到链表中//
voidIntersection(OrderedList&T,OrderedListS1,OrderedListS2)
{
LinkTypep1,p2,p;
if(!
InitList(T))
T.head=NULL;
else
{
p1=S1.head->next;
p2=S2.head->next;
while(p1&&p2)
{
if(p1->datadata)
p1=p1->next;
elseif(p1->data>p2->data)
p2=p2->next;
else
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p1->data;
p->next=NULL;
Append(T,p);
p2=p2->next;
p1=p1->next;
}
}
}//顺次寻找两个集合中相同的元素并输入到新的集合中输出//
}
//求集合S1和集合S2中元素的交集//
voidDifference(OrderedList&T,OrderedListS1,OrderedListS2)
{
LinkTypep1,p2,p;
if(!
InitList(T))
T.head=NULL;
else
{
p1=S1.head->next;
p2=S2.head->next;
while(p1&&p2)
{
if(p1->datadata)
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p1->data;
p->next=NULL;
Append(T,p);
p1=p1->next;
}
elseif(p1->data>p2->data)
p2=p2->next;
else
{
p1=p1->next;
p2=p2->next;
}
}
while(p1)
{
p=(LinkType)malloc(sizeof(NodeType));
p->data=p1->data;
p->next=NULL;
Append(T,p);
p1=p1->next;
}
}
}
//求集合S1和集合S2中元素的补集//
四、调试分析
1.在本次实验中编写将数组中的元素插入到新的链表和将新的结点插入到已知链表中两个插入函数时没注意到两个插入方法的不同导致算法出现问题;
2.在编写函数时有时没有区分大小写导致函数无法调用;
3.2.算法的时空分析:
4.
(1)有序表采用的是有序单链表,并增设尾指针和表的长度两个标识,各种操作的算法时间复杂度比较合理。
InitList,ListEmpty,ListLength,Append和InsertAfter时间复杂度都是O
(1),DestroyList,LocateElem和TraverseList及确定链表中间结点的时间复杂度为O(n),n为链表长度。
(2)基于有序链表实现的有序集的各种运算和操作的时间复杂度分析:
构造有序集算法CreateSet读入n个元素,逐个用LocateElem判定不在当前集合中及确定插入位置之后,才用InsertAfter插入到有序集中,所以时间复杂度是O(n^2)。
求并集算法Union利用集合的有序性将两个集合的m+n个元素不重复地依次利用Append插入到当前并集的末尾,所以时间复杂度为O(m+n)
对于求交集Intersection和求差集Difference作类似分析,它们的时间复杂度也是O(m+n)
销毁集合算法DestroySet和显示集合算法PrintSet的时间复杂度是O(n)
5.本次实验作业先编写主函数,然后依次完善其他各函数,设计思路清晰,条理清楚。
、
五、用户手册
1.本程序的运行环境为DOS操作系统,执行文件为:
jihe.exe
2.进入演示程序后即显示文本方式的用户界面:
3.进入“构造集合1(MakeSet1-1)”和“构造集合2(MakeSet2-2)”的命令后,即
提示输入集合字符串,结束命令为回车符。
4.键入相关菜单操作,程序执行相关代码并输出相关结果。
六、测试结果
1.执行命令”1”:
输入excise后,构造的集合Set1为:
[c,e,i,s,x];
2.执行命令”2”:
输入instruction后,构造的集合Set1为:
[c,i,n,o,r,s,t,u];
3.执行命令”u”:
构造集合Set1和Set2的并集为:
[c,e,i,n,o,r,s,t,u,x];
4.执行命令”i”:
构造集合Set1和Set2的交集为:
[c,i,s];