数据结构课程设计 链表合并.docx
《数据结构课程设计 链表合并.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计 链表合并.docx(20页珍藏版)》请在冰豆网上搜索。
数据结构课程设计链表合并
两个链表的合并
1.课程设计目的
实现对两个的链表的交叉合并,输出线形表C用直接插入排序法对C进行升序排序,生成链表D,并输出链表D。
掌握对线性表的链式表示和实现,实现插入的操作。
了解链表交叉合并的方法和直接插入排序法的思想,并深刻掌握算法的格式和应用。
提高对数据结构的理解和应用,增强个人动手实践能力和逻辑分析能力。
2.设计方案论证
2.1设计思路
本课程设计完成将对链表的交叉合并和直接插入排序的实现。
首先将两个链表进行交叉合并,合并的要求如下:
建立两个链表A和B,链表元素个数分别为m和n个。
假设元素分别为(x1,x2,…xm),和(y1,y2,…yn)。
把它们合并成一个线形表C,使得:
当m>=n时,C=x1,y1,x2,y2,…xn,yn,…,xm
当n>m时,C=y1,x1,y2,x2,…ym,xm,…,yn
输出线形表C。
然后将已合并的链表进行直接插入排序,输出链表D。
2.1.1链表
线性表的链式存储结构简称为链表。
链表的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元是可以连续的,也可以使不连续的)。
因此,为了表示每个数据元素ai与其直接后继元素ai+1之间的逻辑关系,对数据ai除了储存其本身的信息之外,还要存储一个指示其直接后继的信息。
这两部分组成数据元素ai的存储影像,成为结点。
它包括两个域:
其储存元素信息的域称为数据域;存储直接后继位置的域成为指针域。
链表的每个结点只包含一个指针域。
用线性链表表示线性表的时,数据元素之间的逻辑关系是有结点中的指针指示的。
通常我们把链表话画成用箭头相连接的结点的序列,节点之间的箭头表示指针。
在c语言中,链表用结构指针来描述。
//————————线性表的单链表存储结构————————
typedefstructLnode{
ElemTypedata
StructLnode*next;
}Lnode,*LinkLIst;
后皆可。
这就是两个单链表的简单合并操作。
合并的算法如下,
VoidMergeList_L(LinkList&La,LinkList&b,LinkList&Lc){
//已知单链线性表La和Lb的元素按值非递减排列。
//归并}La和Lb得到新的单链表线性表Lc,Lc的元素也按值非递减排列。
pa=La—〉next;pa=Lb—〉next;
Lc=pc+La;
While(pa&&pb){
If(pc—>data<=pb—>data;)
pb—>next=pb;pc=pa;pa—>next;}
elsepc—>next=pb;pc=pb;pb—>next;}}
pc—>next=pa?
pa:
pb;
free(Lb)
在链表的合并中常用的操作是插入,假设要在线性表的两个元素之间出入一个新的元素x。
为了插入元素x,首先要生成一个数据域为x的结点,然后插入数据元素x。
根据插入操作的逻辑定义,还要修改结点a中的指针域,令其指向结点x,而结点x中的指针域应指向结点b,从而实现3个元素a,b和x之间的逻辑变化。
上述指针修改语句描述为,
s—>next=p—>next;p—>next=s;
单链表中插入结点时的指针变化情况如图1
p
s
图1单链表中插入结点时的指针变化情况
反之,在线性表中删除元素b时,为了在单链表中实现元素a、b和c之间逻辑关系的变化,仅需改动节点a中的指针域即可。
假设p为指向节点a的指针,则修改指针的语句为
p->next=p->next->next;
删除节点时指针变化情况如图4所示
图2单链表删除节点时指针变化情况
在已知单链表中元素插入或插入或删除的确切位置的情况下,在单链表中插入或删除一个结点时,仅需修改指针而不需要移动元素。
删除元素代码如下:
StatusListInsert_L(LinkList&L,intI,ElemTypee){
//在带头结点的单链表线性表L中,删除第i个元素,并由e返回其值
p=L;j=0;
while(p&&jp=p—>next;+j:
}
If(!
(p—>next)||j>i-1)returnERRoR;//删除位置不合理
q=p—>next;p—>next=q—>next;//删除并释放结点
e=q—>data;free(q);//释放函数
retrunOK;
}//ListDelete_L
2.1.3链表的排序
排序是计算机程序设计中的一个重要操作,他的功能是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。
由于待排序的记录数量不同,使得排序过程中的存储器不同,可将排序方法分为两大类;一类是内部排序,指的是待排序记录存放在计算机随机存储器中进行的排序过程。
另一类是外部排序,指的是待排序记录的数量很大,,以至内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
在链表的操作中常用到两个标准函数malloc和free。
通常,在设有“指针”数据类型的高级语言中均存在与其相应的过程与函数。
假设p和q是LinkList型的结点,则执行p=(LinkList)malloc(sizeof(LNode))的作用是由系统生成一个Lnode型的结点,同时将该结点的起始位置赋给指针变量p;反之执行free(q)的作用是由系统回收一个LNode
型的结点,,回收后的空间可以备做再次生成结点时用。
因此,单链表和顺序存储不同,它是一种动态结构。
整个可用存储空间可为多个链表共同享用,每个链表占用的空间不需要预先分配划定,而是可以由系统即使生成。
因此建立线性表的链式存储结构的过程就是动态生成链表的过程。
即从“空表”的初始状态起,一次建立个元素的结点,并逐个插入链表。
单练表的建立算法如下,其时间复杂度为O(n×n)。
ViodCreateList_L(LinkList&L,intn){
//逆位序输入n个元素的值,建立带表头结点的单链线性表L。
L=(LinkList)malloc(sizef(LNode));
L—>next=NULL;//先建立一个带头结点的单链表
For(i=n;i>n;--i){
p=(LinkList)malloc(sizeof(LNode)),//生成新结点
scanf(&p—>data);
p—>next=L—>next;L—>next=p;
}
}//CrateList_L
2.1.2链表的合并
链表的合并是将两个链表合并成一个链表。
假设两个链表LA和LB头指针为La,Lb,要归并La和Lb得到链表Lc,pa和pb分别指向La和Lb表中当前待比较插入的结点,
而pc指向Lc当前最后一个结点,若pa—>datadata,则将pa所指结点链接到pc所指结点之后,,否则将pb所指结点链接到pc所指结点之后。
当LA和LB为非空表时,
pa和pb分别指向La和Lb表中第一个结点,否则为空;pc指向空表Lc中的头结点。
由于链表的长度为隐含的,则第一个循环执行的条件pa和pb皆非空,当其中一个为空时,说明有一个表的元素已归并完,则是要将另一个表的剩余段链接在pc所指结点之在内部排序中,如果按照排序过程中依据的不同原则进行分类,则大致可以分为插入排序、交换排序、选择排序、归并排序、和计数排序5类。
通常在排序过程中需进行一下两种基本操作:
(1)比较两个关键字的大小;
(2)将记录从一个位置转移到另一个位置。
前一个基本操作是对于任何排序方法来说都是必要的,而后者可以通过改变记录的储存方式来来予以避免。
且为了研究方便起见,设关键字均为整数。
待排序、记录的数
typedefstruct{
KeyTypekey;//关键字项
InfoTypeotherinfo;//其他数据项
Typedefstruct{
RedTyper[MSXSIZE+1];//r[0]闲置或用作哨兵单元
Intlength;//顺序表长度
}SqList//顺序表类型
本课程设计主要是应用直接插入排序将合并的链表进行排序。
直接插入排序是一种最简单的排序方法,他的基本操作是将一个记录插入已排好序的有序表中,从而得到一个新的、数据记录增一得有序表。
一般情况下,第i趟直接插入排序的操作为:
在含有i-1个记录的有序子序列r[1.i-1]中插入一个记录r[i]后,变成含有i个记录的有序子序列r1.i[];并且和顺序表查找类似,在r[0]处设置监视哨。
在自i-1起往前收索的过程中,可以同时后已记录。
整个排序过程为进行n-1趟插入,即:
先将序列中的第一个记录看做一个有序的子序列,然后从第二个记录起逐个插入,直至整个序列变成按关键字有序序列为止。
链表直接插入排序的算法如下图3所示
voidInsertSort(SqList&L){//对顺序表L作直接插入排序。
For(i=2;i<=L.length;++i)
If(LT(L.r[i].Key,L.r[i-1].Key)){//“(”,需要将L.r[i]插入有序子表
L.r[0]=L.r[i]//赋值为哨兵
L.r[i]=L.r[i-1];
For(j=i-2;LT(L.r[0].Key,L.r[j].Key);--j)
L.r[j+1]=L.r[j];//记录后移
L.r[j+1]=L.r[0];//插入到正确位置
}
}//InsertSort
2.2算法设计
程序的基本功能,首先建立建立单链表,向单链表中输入元素,然后输出单链表,将两个单链表进行合并。
先建立两个升序链表,假设分别为La=(x1,x2,…xn),Lb=(y1,y2,…yn),它们的长度分别为a,b。
若a>b,则把这两个链表合并后,要求新Lc=x1,y1,x2,y2,…xn,yn…,。
若a
本程序的主要特点是运用结构体和链表,其中在建立链表的时候通过手动输入链表节点元素,再由定义的函数完成链表的归并,最后输出两个链表的归并结果。
如果两个链表均为空表,则显示NULLLIST。
数据的主要流程,在主函数中创建链表A和链表B。
首先输入A链表的长度,然后主函数调用creat函数,creat函数中有malloc函数创建头结点,再由for循环控制所创建链表的长度,向已建好的链表中添加数据元素,当链表长度达到所规定的长度时则跳出函数,将链表输出,程序继续向下执行,提示输入链表B的长度,方法与创建练表A相同。
在创建链表结束后,由主函数调用hebing函数,对两个链表进行归并。
本程序是完成对连个链表的交叉合并的实现,首先判断链表A和链表B的长度的大小,设链表A的长度为a,链表B的长度为b,若a>b,则新表Lc=x1,y1,x2,y2,…xn,yn…;若a
设置两个指针分别指向A和B,并对A和B节点进行比较,若A结点的数据元素大于B结点的数据元素,则将A结点存入到C链表中,A链表指针后移,B链表指针不动,继续下一次比较,当A链表或B链表为空时,则将剩余的数据元素依次存入C链表后。
若输入的两个链表均为空链表,则显示链表为空。
程序流程图如图3所示:
图3设计程序流程图
图4调用函数hebing流程图
调用函数print的是用来输出链表的函数,用以循环实现对链表的输出,流程图如图5所示
图5调用函数print流程图
(1)创建链表函数的设计
创建链表,首先由主函数输入要创建的链表长度n,在由主函数将参数传递到创建链表函数creat,由For循环控制表节点的创建,由malloc函数开辟新的结点空间,创建链表结束后,由print函数将链表元素输出。