东北大学数据结构实验报告.docx
- 文档编号:8647623
- 上传时间:2023-02-01
- 格式:DOCX
- 页数:25
- 大小:154.12KB
东北大学数据结构实验报告.docx
《东北大学数据结构实验报告.docx》由会员分享,可在线阅读,更多相关《东北大学数据结构实验报告.docx(25页珍藏版)》请在冰豆网上搜索。
东北大学数据结构实验报告
实验报告
课程名称:
数据结构
班级:
实验成绩:
实验名称:
顺序表和链表的应用
学号:
批阅教师签字:
实验编号:
实验一
姓名:
实验日期:
2017-11-25
指导教师:
组号:
实验时间:
18:
30~22:
30
一、实验目的
(1)掌握线性表的基本操作(插入、删除、查找)以及线性表合并等运算在顺序存储结构、链式存储结构上的实现。
重点掌握链式存储结构实现的各种操作。
(2)掌握线性表的链式存储结构的应用。
二、实验内容与实验步骤
(1)实验内容:
实现约瑟夫环,约瑟夫环(Joseph)问题的一种描述是:
编号为1、2、3……n的n个人按照顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数的上限值m,从第一个人开始按照顺时针的方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他的顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
设计一个程序求出出列顺序。
(2)抽象数据类型和设计的函数描述,说明解决设想。
首先定义一个链表,用其中的data项存储每个人的编号,用password项存储每个人所持有的密码,并且声明一个指针。
之后使用CreatList_CL函数来创建一个循环链表,在其中的data和password中存入编号和密码,最后使最后一个节点的next指向L,使其能够形成循环队列。
定义了函数Display来显示链表当中的内容,以确定存储的数据没有错误。
定义了函数Delete_L来实现约瑟夫环中依次删除的功能,依次比较,如果某个人所持的密码和m值相等,则删除这个结点,并且输出此时该结点的编号和密码,实现出列的功能。
(3)简短明确地写出实验所采用的存储结构,并加以说明。
该实验我主要采用的是线性表的链式存储结构,首先定义了链表的结构,其中包括data项和password项,分别存储每个人的编号和所持密码,还声明了指向下一个结点的指针,该指针可以连接各个结点,并且将最后一个结点的指针指向第一个结点使之成为一个循环链表。
三、实验环境
操作系统:
Windows7
调试软件名称:
VisioStudio2017
上机地点:
信息楼B405
四、实验过程与分析
(1)主要的函数或操作内部的主要算法,分析这个算法的时、空复杂度,并说明设计的巧妙之处。
本实验中主要的函数包括创建链表、显示链表内容和出列过程四个部分。
主要函数的代码如下:
创建链表:
typedefintDatatype;
typedefstructnode//链表的定义
{
Datatypedata;
intpassword;
structnode*next;
}ListNode,*CLinkList;
voidCreatList_CL(CLinkList*L,intn)//创建一个链表
{
inti,pin;
CLinkListp,q;
(*L)=(CLinkList)malloc(sizeof(ListNode));
if((*L)==NULL)
printf("error\n");
else
(*L)->next=NULL;
q=*L;
for(i=0;i { p=(CLinkList)malloc(sizeof(ListNode)); if(p==NULL) printf("error\n"); printf("请输入第%d个人的密码: ",i+1); scanf("%d",&pin); p->data=i+1; p->password=pin; q->next=NULL; q->next=p; q=p; } q->next=(*L)->next;//指向L结点,形成 } 创建这个链表的时间复杂度为O(n),空间复杂度为O(n2)。 显示链表中的信息内容: voidDisplay(CLinkList*L,intn) { inti; CLinkListp; p=(*L)->next; printf("\n显示链表内容\n"); for(i=0;i { printf("编号: %2d密码: %d\n",p->data,p->password); p=p->next; } } 该算法的时间复杂度为O(n),空间复杂度为O(n2)。 删除结点,完成出列功能: voidDelete_L(CLinkList*L,intn,intm) { inti=0,j; CLinkListp,q; q=(*L); p=(*L)->next; printf("\n删除的顺序: \n"); while(i { for(j=0;j { q=p; p=p->next; } printf("编号: %d密码: %d\n",p->data,p->password); m=p->password; q->next=p->next; free(p); p=q->next; n--; } } 该算法的时间复杂度为O(n2),空间复杂度为O(n2)。 该设计的巧妙之处在于并不需要额外的空间来存储数据,因而空间复杂度较低,而且线性表的链式存储结构可以用物理位置上的邻接关系来表示结点间的逻辑关系,这样使读者在阅读代码的过程中可以更加方便和便于理解。 它可以随机存取表中的任一结点,还可以免插入和删除操作带来的大量的结点的移动,能给结点动态分配内存,这样就不存在存储空间不足的情况,而且循环链表还可以方便的从链表的最后一个结点遍历到链表的第一个结点。 使操作更加方便。 (2)你在调试过程中发现了怎样的问题? 又做了怎样的改进 1)在最开始的调试阶段,我发现链表插入结束之后,不能按照正常情况下输出链表的内容,只能正常显示第一个人的数据,在显示第二个人的信息是数据为乱码。 之后我发现,在插入链表的过程中,我是在执行循环插入数据的循环中将结点的指针指向了第一个结点,因而,在进行链表显示的过程中,第二个结点的内容不是正常的数据。 之后我将q->next=(*L)->next;这条指令放到了整个插入循环的外部,这样表示在插入所有数据之后,最后一个结点的指针指向了第一个结点,形成了一个循环队列,此时链表的数据显示正确。 2)再次调试时,我发现人员出列时,只有第一个人出列正常,在第二个人出列时程序自动终止,不能正常显示之后出列的人的信息,并且程序自动终止运行,经过检查我发现在经过一次删除后,没有将指针指向下一个结点,因而出现问题。 经过更改,程序运行正常。 3)在实验的开始阶段,数据遍历总是出现问题,经过查找资料我发现了约瑟夫环头结点的特殊性,因此我不再使用头结点,程序便恢复正常了。 (2)测试结果 五、实验结果总结 回答以下问题: (1)你的测试充分吗? 为什么? 你是怎样考虑的? 答: 我认为我的测试充分,因为我随机选用了很多组不同的数据进行测试,并且每次测试的结果都是正确的答案,这样选取的数据具有很强的随机性,具有代表性,因而我认为我的测试比较充分。 (2)你的存储结构的选取是不是很适合这个应用? 为什么? 答: 我认为我选取的线性链式存储结构适合这个应用,因为首先此题中描述的情景中表示人们按照顺时针的方向进行排队,此时头尾相连,这与循环链表的结构十分相似,使用循环链表的结构,这样可以很方便的从链表的最后一个结点访问到链表的第一个结点,并且这样的存储方式是用物理位置上的邻接关系来表示结点间的逻辑关系,根据这个特点,该种结构可以随机存取表中的任一结点,而且它也可以避免插入和删除操作带来的大量结点的移动,并且可以随时分配空间和释放空间,这样可以减少空间的使用量,并且可以做到灵活的扩充空间,这样的结构很适合这个应用。 (3)用一段简短的代码及说明论述你的应用中有关插入和删除元素是如何做的? 答: 插入元素: 首先定义了两个临时指针p和q来分别表示新插入结点的指针和第一个结点的指针,在每次插入之前应该动态的分配内存,输入要输入的信息,并且将各种数据存储到链表中相应的项里,将前一个结点的next赋值为空,再将前一个结点的指针指向下一个结点,此时完成一个元素的插入。 依次类推,运用循环来实现所有人的数据的插入,关键代码如下: p=(CLinkList)malloc(sizeof(ListNode)); if(p==NULL) printf("error\n"); printf("请输入第%d个人的密码: ",i+1); scanf("%d",&pin); p->data=i+1; p->password=pin; q->next=NULL; q->next=p; q=p; 删除元素: 进行循环来实现每个元素出列的功能,首先每个人进行循环,一次进行报数,在报到m-1之前都不进行删除元素这个动作,在m时,把此时结点中的password中的数值赋给m然后运用q->next=p->next;将结点删除,同时释放结点p,将人数减1,以此类推完成所有的删除操作,直到所有的元素出列,关键代码如下: while(i { for(j=0;j { q=p; p=p->next; } printf("编号: %d密码: %d\n",p->data,p->password); m=p->password; q->next=p->next; free(p); p=q->next; n--; } (4)在你的应用中是否用到了头结点? 你觉得使用头结点为你带来方便了吗? 答: 在我的应用中我没有用到头结点。 在实验的一开始,我使用了头结点,但是使用头结点给数据的遍历带来了困难,因此我便放弃使用头结点。 (5)源程序的大致的执行过程是怎样的? 答: 首先用编译器编写一个.c的文件,然后编译生成.obj的文件,通过连接将目标文件连接生成一个.exe文件,之后运行文件就可以执行了。 六、附录 (1)实验设想和建议 这次实验提高了我对数据结构中关于循环链表和顺序表的理解,提高了我的编程能力,学校以后最好可以增加实验课的课时,这样我们可以更大程度的提高自己的编程能力。 另外我认为该实验不仅可以使用使用链表指针来实现,还可以使用数组来模拟链表来实现约瑟夫环,用数组的下标来指向前一个和后一个元素,之后进行删除来实现约瑟夫环。 (2)参考资料: 《数据结构(第二版)》闫玉宝编著清华大学出版社 实验报告 课程名称: 数据结构 班级: 实验成绩: 实验名称: 栈、队列、字符串和数组 学号: 批阅教师签字: 实验编号: 实验二 姓名: 实验日期: 2017-11-20 指导教师: 组号: 实验时间: 18: 30~22: 30 一、实验目的 (1)掌握栈、队列、串和数组的抽象数据类型的特征。 (2)掌握栈、队列、串和数组的抽象数据类型在计算机中的实现方法。 (3)学会使用栈、队列来解决一些实际的应用问题。 2、实验内容与实验步骤 (1)实验内容: 假设表达式中除了变量名、常量和运算符外,还可以允许两种括号: 圆括号和中括号,其嵌套的次序随意,编写程序检验输入的表达式中括号的的顺序是否合法。 (2)描述抽象数据类型或设计的函数描述,说明为什么要使用这种抽象数据类型,并说明解决设想。 抽象数据类型或函数描述: 首先定义了一个结构体并且声明为栈类型,在其中定义了空间基地址的指针、栈顶指针以及栈存储空间的大小。 之后设计了Creat_Stack的函数,用此函数来创建一个空栈,这样可以使用堆栈来实现括号匹配的功能,又设计了一个名为Stack_Full的函数了来判断栈是否已满,若栈未满才可继续之后的压栈功能,如果堆栈已满,则需要使用realloc来动态分配空间,扩大栈的存储空间。 我还设计了一个名为empty的函数,用它来判断堆栈是否为空,堆栈为空或不为空时分别返回0或1。 之后设计了名为push和pop的函数来实现括号的入栈和出栈,之后设计了名为Match的函数,来判断括号是否匹配,设计了名为clean的函数来清空堆栈,这样可以连续判断不同的多项式的括号是否匹配。 解决设想: 对于本题,首先我使用了栈结构,利用栈中数据“先进后出”的特点来实现对括号是否匹配的检验。 实现过程基本如下: 从左到右依次扫描多项式,如果遇到左括号便将左括号入栈,在所有左括号入栈之后便可以扫描到右括号,如果扫描到的右括号和栈顶的左括号可以匹配时,将左括号出栈,以此类推,最后判断栈是否为空,若为空,则括号匹配,否则括号不匹配。 3、实验环境 操作系统: Windows7 调试软件名称: VisioStudio2017 上机地点: 信息楼B405 四、实验过程与分析 (1)实现时,主要的函数或操作内部的主要算法,分析这个算法的时、空复杂度,并说明设计的巧妙之处。 主要函数或操作内部的主要算法: typedefstruct//栈的声明 { char*base;//指示存储数据元素的空间基地址的指针 char*top;//栈顶指针 intstacksize;//栈存储空间大小,以数据元素为单位 }SStack; voidCreat_Stack(SStack*s)//创建空栈 { s->base=(char*)malloc(sizeof(char)*size); if(s->base==NULL) printf("error\n"); else { s->top=s->base; s->stacksize=size; } } 上面的算法用来建立栈,该算法的时间复杂度为O (1),空间复杂度为O(n)。 intStack_Full(SStack*s)//判断栈是否为满 { if(s->top-s->base>=100) return1; else return0; } intempty(SStack*s)//判断栈是否为空 { if(s->base==s->top) return0; else return1; } 上面的算法分别用来判断栈是否已满,栈是否为空栈,上面两个算法的时间复杂度和空间复杂度均为O (1)。 voidpush(SStack*s,char*str)//入栈 { if(Stack_Full(s)! =0) { printf("full\n"); } else *s->top++=*str; } voidpop(SStack*s,char*str)//出栈 { if(s->base==s->top) printf("Thestackisempty\n"); else *str=*--s->top; } 上面两个算法用来实现数据的入栈和出栈,时空复杂度均为O (1)。 voidMatch(SStack*s,char*str) { inti,j; chart; j=strlen(str); for(i=0;i { if(str[i]=='('||str[i]=='[') push(s,str); } for(i=0;i { if(str[i]==')') { if(*s->top=='(') { pop(s,&t); } else s->top=s->top-1; } if(str[i]==']') { if(*s->top=='[') { pop(s,&t); } else s->top=s->top-1; } } if(empty(s)==0) printf("括号匹配! \n"); else printf("括号不匹配! \n"); } 该Match函数的作用即判断括号是否匹配,是本程序的核心函数,若假设输入的表达式的长度为n,则此函数中进行了两次循环,一次为扫描左括号使其全部入栈,另外一次为扫描右括号并且判断新扫描出来的右括号与栈顶的左括号是否匹配。 在整个过程中执行了2n次循环,因此此程序的时间复杂度为O(n)。 对于空间复杂度,本算法存储了长度为n的表达式,因而该算法的空间复杂度为O(n)。 设计的巧妙之处: 在本程序中我使用了栈这种抽象数据类型,栈的“先进后出”的特点与检验括号是否匹配的“期限待的急迫程度相吻合,设计顺序栈来解决括号匹配问题。 如果单从括号检验这个目的考虑可以有多种方法来实现该实验目的,而使用栈来实现括号匹配的检测,简化了程序的设计,比较容易理解和实现,并且可以提高时间效率。 (2)你在调试过程中发现了怎样的问题? 又做了怎样的改进? 1)在开始程序编译时,编译器总是提醒函数的形式参数的写法有问题,之后我发现,我在栈声明时将SStack未声明为指针类型,而我在形式参数中将参数写成了SStacks,因而出现错误,所以我将其更改为SStack*s,这个问题得以解决。 2)之后,编译器进行编译时,编译器提醒我在调用empty,match等函数时,实际参数的输入有问题,使得编译不能够通过,经过检查我发现我在写这些函数时,形式参数定义为char*s,因而我便在实际参数中代表字符串的参数前加取地址&符号,这个问题便解决了。 3)在进行编译时还出现了这样的警告,说我的小于号没有定义或者没有匹配,经过检查我发现我在循环中将其中一个条件写成 (3)你的抽象数据类型的实现是否具有可扩展性? 我的抽象数据类型具有可扩展性,因为栈的大小可以修改,如果栈已满,则可以增加空间,因此具有可扩展性。 (4)测试结果 五、实验结果总结 回答以下问题: (1)你的测试充分吗? 为什么? 你是怎样考虑的? 答: 我认为我的测试充分,因为我的表达式是随机给出的,这样选择的数据具有随机性,具有很强的代表性,并且每次的结果正确,因此我认为我的测试比较充分。 (2)为什么你要选用栈或队列或字符串或数组等抽象数据类型作为你应用的数据结构? 答: 我使用了栈这种抽象数据类型作为我应用的数据结构,栈是一个只能访问表的尾端数据的数据集合,是一种在表的一端进行插入和删除操作的线性表,数据具有“先进后出”的特点,而这种特点和括号匹配中检验括号的“期限待的极限程度”这个特点相符合,因此选用栈这种数据结构可以简化程序,更好的理解和实现程序,提高了程序运行的时间效率。 (3)用一段简短的代码及说明论述你的应用中主要的函数的主要处理部分。 答: 下面的代码部分为Match函数中的主要处理部分,使用了两个循环来处理输入的表达式,第一个循环是用来从左到右扫描表达式遇到“(”或者“[”就将括号入栈,第二个循环是用来扫描表达式,如果遇到“)”或“]”就将其与栈顶的括号进行匹配,如果匹配,就将栈顶的左括号弹出,如果不匹配就将栈顶指针向下移动,直到所有的括号操作完成,如果栈为空,那么该表达式的括号是匹配的,否则括号不匹配。 j=strlen(str); for(i=0;i { if(str[i]=='('||str[i]=='[') push(s,str); } for(i=0;i { if(str[i]==')') { if(*s->top=='(') { pop(s,&t); } else s->top=s->top-1; } if(str[i]==']') { if(*s->top=='[') { pop(s,&t); } else s->top=s->top-1; } } (4)你的应用中采用的是顺序的还是链式的存储结构? 为什么要选用这种存储结构。 答: 我的应用中采用的是顺序的链式存储结构,因为对于栈这种抽象的数据类型,有着“先进后出”的特点,这种特性和表达式中括号匹配的过程相符合,因此我采用了这种存储结构。 (5)源程序的大致的执行过程是怎样的? 答: 先用编译器编写一个.c的文件,然后编译生成.obj的文件,通过连接将目标文件连接生成一个.exe文件,之后运行文件就可以执行了。 六、附录 实验参考的资料《数据结构(第二版)》闫玉宝编著清华大学出版社 思考题 (a)栈和队列在计算机系统中有哪些应用? 写出你知道的系统中,这两种抽象数据类型的应用。 答: 在计算机系统中,使用栈的应用有表达式的计算,迷宫以及括号匹配等。 使用队列的应用有打印文档,售票系统,解决主机与外部设备之间速度不匹配问题,解决多用户引起的资源竞争问题等 (b)在程序调用的时侯,需要进行函数的切换,你认为函数在进行切换时系统要做那些工作? 答: 对于函数的切换,主要是一个压栈的过程,先以一种约定的方式把参数压栈,然后根据函数地址调用函数,函数执行后根据约定的方式出栈取得参数。 实验报告 课程名称: 数据结构 班级: 实验成绩: 实验名称: 栈、队列、字符串和数组 学号: 批阅教师签字: 实验编号: 实验三 姓名: 实验日期: 2017-12-7 指导教师: 组号: 实验时间: 18: 30~22: 30 一、实验目的 (1)理解分治法的思想。 (2)掌握用分治法解决问题 二、实验内容 (1)仔细阅读备选实验的题目,选择一个(可选多个)作为此次实验题目,设计的程序要满足正确性,代码中有关键的注释,书写格式清晰,简洁易懂,效率较高,利用C++的模板,设计的程序通用性好,适合各种合理输入,并能对不合理输入做出正确的提示。 (2)归并排序 问题描述 目前的网上拍卖系统会显示很多待拍卖的物品,通常这些系统具有按照某个关键字对打出的广告进行排序列出的功能,并且能够按照用户输入的某个关键字进行过虑,找到某些特定的物品。 编程任务 定义一个Advertisement类,该类中至少包含该物品的数量,名称,联系人e-mail,最好有开拍时间及关闭时间,根据用户输入的关键字比如名称,mail,时间等,利用非递归的归并排序对所有的广告进行排序,并列出所有排好序的广告。 数据输入 由文件input.txt提供输入的所有广告信息。 程序中由用户输入要排序的关键字。 结果输出 程序运行结束时,排好序的广告输出到文件output.txt中,并为每个广告添加序号。 输入文件示例 输出文件示例 input.txt output.txt Coat(物品名称) 3(数量) a@ Skirt 5 b@ Cap 7 c@ Bag 12 a@ Title(用户输入按照title排序) 1 Bag 12 a@ 2 Cap 7 c@ 3 Coat(物品名称) 3(数量) a@ 4 Skirt 5 b@ 三、实验环境 操作系统: Windows7 调试软件名称: VisioStud
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 东北大学 数据结构 实验 报告