实验题目:线性表的关键算法实现及性能分析(预习报告)
一、实验目的
1. 能够运用高级程序设计技术实现线性表及其关键算法(实验1、2、3、4、5)
2. 能够通过对比分析的方法分析插入、删除等操作对线性表的关键算法性能的影响规律(实验4、5)
3. 具体任务参加各个实验的任务书
二、实验原理
1.线性表的基本原理
(1)线性表是具有相同特性的数据元素的一个有限序列。即由n(n>=0)个数据元素(结点)组成的有限序列。(除了第一个和最后一个元素,其余元素有且仅有一个前驱和后继。)
(2)特性:其中数据元素的个数n定义为表的长度;
当n=0时称为空表;将非空的线性表(n>0)记作:(a1,a2,a3…an);
数据元素ai(1<=i<=n)只是一个抽象的符号,其具体含义在不同的情况下可以不同。
(3)抽象数据类型定义;
ADT List
{ 数据对象: D={a; |a;∈ElemSet, i=1,2,.... n, n>=0}
数据关系: R1={<a:i-1,a;>| ai-1,a:∈D, i=2,...n}
基本操作:
- IniList (&L)//构造空表L。
2. GetElem(L, i, &e)//取元素ai,由e返回a[i]
3. PriorElem(L, ce, &pre_ e) //求的ce前驱,由pre_ e返回
4.ListLength (L)//求表L的长度
5. EmptyList(L)//判断线性表是否为空
6.ClearList(SqList &L)//销毁线性表
7.LoCateELem(SqList L,ElemType e)//根据元素值查找对应结点
8. InsertElem(&L, i,e) // 在元素ai之前插入新元素e
9. DeleteElem(&L, i)//删除第i个元素
10.DestroyList(SqList &L)//清空线性表
}ADT List
2.关键算法设计原理及性能影响因素分析
顺序存储结构中在进行删除和插入操作时,会对插入元素的位置进行判断,最好的情况下仅需要判断一次,但在最坏的情况下需要遍历整个数组。在链式存储的结构中,添加和删除节点时无需进行整体的后移或者前移,只需要更改指针,但是在访问结点元素是的时间消耗可能较大,需要一个个遍历。
三、实验方案设计
1.存储方案设计
分析:(1)顺序存储:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构;顺序存储结构中在进行删除和插入操作时,会对插入元素的位置进行判断,最好的情况下仅需要判断一次,但在最坏的情况下需要遍历整个数组;
(2)链式存储:在链式存储的结构中,添加和删除节点时无需进行整体的后移或者前移,只需要更改指针,但是在访问结点元素是的时间消耗可能较大,需要一个个遍历;
对于第一个实验目标插入和删除通过一一比对的形式进行操作,即遍历整个顺序表来进行。对于第二个实验进行顺粗虚标的逆置,通过对不同的位置的元素进行位置改变,可以通过前后交换的方式进行,一次减省了对线性表前后移动的操作,节省时间和空间。对于第三个实验目标在顺序表的区间删除时时间复杂度较高,容易超时,所以采用双线性表的形式,将符合要求的元素保存在另外的顺序表内,优势是降低时间复杂度,大大减少的运算时间,但是增加了程序的空间占用,申请了两个线性表的空间,需要更大的存储空间。
最终根据实验题目的具体要求,选择顺序存储。
2.算法的设计和实现
(1)构造空表(操作结果:构造一个空的线性表L)
Status InitList_ Sq(SqList &L)//构造一个空的顺序表L
{
L.elem=new ElemType[MAXSIZE]; // 为顺序表分配空间
if(!L.elem) exit(OVERFLOW);//存储分配失败
L.length=0;//空表长度为0
return OK;
}
(2)清空线性表(初始条件:线性表L已经存在;操作结果:将线性表L重置为空表)
void ClearList(SqList &L)
{
L.length=0;//将线性表的长度置为0
}
(3)销毁线性表(初始条件:线性表L已经存在;操作结果:销毁线性表L)
void DestroyList(SqList &L)
{
if (L.elem) delete[]L.elem;//释放存储空间
}
(4)求线性表长度
int GetLength(SqList L)
{
return (L.length);
}
(5)判断线性表是否为空
int IsEmpty(SqList L)
{
if (L.length==0)
return 1;、
else return 0;
}
(6)取元素a[i],由e返回a[i]
int GetElem(SqList L,int i,ElemType &e)
{
if (i<1|i>L.length) return ERROR;//判断i值是否合理,若不合理,返回ERROR
e=L.elem[i-1]; // 第i-1的单元存储着第i个数据
return OK;
}
(7)根据元素值查找对应结点
int LoCateELem(SqList L,ElemType e)
{
for (i=0;i< L.length;i++)
if (L.elem[i]==e) return i+1;
return 0;
}
- 插入元素(初始条件:线性表L已经存在,1<=i<=ListLength(L)+1;操作结果:在L的第i个位置之前插入新的数据元素,L的长度加1)
Status ListInsert_ Sq(SqList &L,int i ,ElemType e)
{
if(i<1 |I i>L.length+ 1) return ERROR;//i值不合法
if(L.length==MAXSIZE) return ERROR; // 当前存储空间已满
for(j=L.length-1;j>=i-1;--)
L.elem[j+1]=L.elem[j]; // 插入位置及之后的元素后移
L.elem[i-1]=e; // 将新元素e放入第i个位置
++L.length;//表长增1
return OK;
}
算法分析:在插入元素时,消耗时间较长,最快时插入在最后,最慢时要遍历整个数组,时间消耗较大
- 删除元素(初始条件:线性表L已经存在,1<=i<=ListLength(L),操作结果:删除L的第i个数据元素,并返回其值,L的值减1)
Status ListDelete_ Sq(SqList &L,int i)
{
if(i<1)l(i>L.length)) return ERROR; //i 值不合法
for (i=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j]; // 被删除元素之后的元素前移
L.length--;/1表长减1
return OK;
}
3.测试方案
测试用线性表元素:1 3 5 7 9 11 13 15 17 19 21 23 25 , 共13个数
①测试方式:根据元素值查找对应结点,输入元素值,返回位置i
输入测试数据:15
测试结果:8
②测试方式:调用删除功能的函数,输入删除的位置,返回插入后的元素组
输入测试数据:5
测试结果:1 3 5 7 11 13 15 17 19 21 23 25
③测试方式:调用插入功能的函数,输入插入的位置和元素,返回插入后的元素组
输入测试数据:6 12
测试结果:1 3 5 7 9 11 12 13 15 17 19 21 23 25
④测试方式:根据元位置查找对应元素值,输入位置i,返回元素值
输入测试数据:9
测试结果:17
⑤测试方式:给定表头结点,判断其长度
输入测试数据:&L
测试结果:13
⑥测试方式:判断该线性表是否为空,给定表头结点,若为空返回1,否则为0
输入测试数据:&L
测试结果:0
实验题目: 线性表实验
- 概述
本次实验的内容是与线性表相关。包括对顺序表的操作、如查找、插入、删除等内容,实现顺序表的创建和就地逆置、线性表元素的区间删除等内容。在本次实验中,首先建立一个空的静态顺序表,然后键盘输入数据存入表中,通过不同的数字输入,实现对不同顺序表删除、插入、查找、显示等操作,顺序表的插入与删除操作类似,在插入与删除后,都要循环调整后面数组的每一位元素,同时记录数据元素的长度的标示符也要跟着改变,查找操作是直接取数组中的查找位输出。链表的删除、插入操作是类似的,要考虑到加入或减少一个结点后,前后结点的链接关系,以及删除或插入的是最后一个结点时,新空间的开辟与结点收尾等问题,其中删除功能的一部分就是查找功能,显示功能也是从链表的头结点遍历至最后一个,依次输出。对于顺序表操作集的实验的插入和删除通过一一比对的形式进行操作,即遍历整个顺序表来进行。对于顺序表创建和就地逆置实验通过对不同的位置的元素进行位置改变,可以通过前后交换的方式进行,一次减省了对线性表前后移动的操作,节省时间和空间。对于线性表的区间删除实验在顺序表的区间删除时时间复杂度较高,容易超时,但是在顺序表的区间删除时时间复杂度较高,容易超时,所以采用双线性表的形式,顺序表优点是空间利用率高,数据是连续存放,命中率比较高,存取速度高效,通过下标来直接访问。缺点是插入和删除比较慢,在插入或者删除一个元素的时候,需要遍历整个顺序表移动前后的数据,而且需要预先分配足够大的空间,估计的过大,会导致顺序表后的大量空间被闲置;但是估计的过小,又会造成溢出。 链表的优点是插入和删除速度快,保留原有的物理顺序,在插入或者删除一个元素的时候,只需要改变指针指向即可。但缺点是查找速度比较慢,因为在查找时,需要循环链表。
二、实验过程
1.调试分析
(1).由于在输入代码时误将等于操作符写成了赋值操作符,导致运行错误。
- .对于指针内部的变量引用,要用箭头符号而不是点符号,因此产生了错误,于是在原来程序中的“->”改为了“.”操作符号,最后运行成功。
- 对于b的赋值,不应直接将指针的值付给b,而是应该将指针L.elme赋值给b,所以应该将L.elme系列变为*(L.elme),就可以正常运行。
- 返回的大小写错误,在c语言中,大小写分别是两个不同的变量,大小写的不同使其蕴含的意思也完全不相同,于是在该函数的返回中返回来“t”这个不存在的值,所以会进行报错,解决方法为将“t”改为“T”。
2.测试过程
对于不同的实验应该有不同的输入,所以将实验分为三个大部分,一个实验对应一个部分,并且每一部分应有不同的测试数据。
实验一:线性表的操作集
该实验主要考察对线性表的操作部分,因此要有大量的的测试数据才能够进行测量。这里只进行两个测试数据的结果展示。
第一个测试数据:
7个数字,分别为10、9、8、7、6、5、4,
进行查找时输入5和7
在进行删除时进行第9和6位置的删除
运行结果如下图:
第二个测试数据:
6个数字,分别为4、5、6、9、1、2,
进行查找时输入5和7
在进行删除时进行第7和1位置的删除
运行结果如下图:
由以上两个图可以得到,运行结果和预期结果完全相符,所以该程序测试完毕且无误。
实验二:线性表的创建和就地逆置
逆置的操作主要在于对线性表线性结构的分析,所以采取前后调换的方式进行就地逆置,本次测试依然采取展示两组测试数据。
第一个测试数据:
共7个元素,分别为9、5、6、3、2、4、1
运行结果如下图:
第二个测试数据:
共6个元素,分别为8、6、5、3、9、1
运行结果如下图:
实验三:线性表的区间删除
在添加裁判程序中的ReadInput()和 PrintList( List L )两个函数后,进行测试,以为区间删除没有特例,所以进行一次测试就可以证明程序没有出现问题。
测试数据:
共6个元素,分别为19 85 45 36 21 15
运行结果如下图:
三、评价分析
1.实验结果分析
实验过程中,对程序的调试基本达到了预期的效果,对于顺序表的插入,在保证不发生“溢出”的前提下,成功的插入了相应的元素。查找相关元素时,返回了相应元素的位置,从而达到了查找相关元素的位置的目的,删除元素的操作时,忽略了顺序表的长度,从而导致了删除失败,但在重新检查程序的过程中进行了修改,从而在第二次调试的过程中成功的删除了设定的元素。
在地址出现问题时,循环次数的不同会导致所输出的地址发生错乱,因此需要对其进行调整和检查,最终找到了解决问题的方法。
2.算法性能评价
线性表的算法较为单一,所以重点在于插入和删除的算法进行优化和改善,因为在插入和删除是不可避免的要进行线性表中元素的改变和移动,索引在其中就更加注重算法的优化的改良。其中,第三个实验中,由于要一次删去所有满足条件的线性表元素,在运用传统的算法时要进行不断的循环语句,大大增加了程序的时间复杂度。
所应邀重点在于修改循环条件或者做出更进一步的删除循环操作,所以要将判断和返回值做出修改。
用过这种算法优化,大大减少了运算短时间,时间复杂度也得到了降低,并且随着数据的增多,这种优化的节省的时间也将更加明显。
- 总结与体会
对于线性链表和顺序表都属于线性表问题,但是线性链表的插入删除比顺序表要简单,方便。顺序表的查找比较方便,线性链表做元素寻找时,必须从头结点开始寻找。各有各的优缺点。顺序表的结构比较简单,逻辑上相邻的元素在物理上也是相邻的。而且在不使用指针的前提下节省了很多的储存空间。在进行插入和删除的实验时,移动了大量的元素,这十分消耗时间。同时还需要考虑到插入元素时是否溢出的问题。经过这次实验,对于线性表的顺序结构的相关代码已基本熟悉,算法知识得到了复习与巩固。在写代码的过程与调试中,在解决问题过程中,丰富了个人编程的经历和经验,提高了个人解决问题的能力。但是在编写程序的过程中关于c语言的指针方面的内容还不能得心应手,这也影响了对数据结构这门课的理解和掌握。所以还要多练习,达到熟能生巧。