




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第2章线性表2.1线性表的基本概念2.2顺序表2.3单链表和循环单链表2.4双链表和循环双链表2.5线性表的应用第2章线性表1/1332.1线性表的基本概念2.1.1线性表的定义线性表是由n(n≥0)个相同类型的数据元素组成的有限序列。当n=0时为空表,记为()或Φ.当n>0时,线性表的逻辑表示为(al,a2,…,ai,…,an),也可以用下图所示的逻辑结构图表示。2.1线性表的基本概念a1a2aian……2/133a1a2aian……开始元素尾元素逻辑序号或位置为i逻辑特征:若至少含有一个元素,则只有唯一的开始元素和终端元素除了起始元素外其他元素有且仅有一个前驱元素除了终端结点外其他元素有且仅有一个后继元素2.1线性表的基本概念3/133线性表中的每个元素有唯一的序号(逻辑序号),同一个线性表中可以存在值相同的多个元素,但它们的序号是不同的。如一个整数线性表:(1,3,2,4,2,1,5)序号为1序号为62.1线性表的基本概念4/1332.1.2线性表的基本运算初始化InitList(L)。其作用是建立一个空表L(即建立线性表的构架,但不含任何数据元素)。销毁线性表DestroyList(L)。其作用是释放线性表L的内存空间。求线性表的长度GetLength(L)。其作用是返回线性表L的长度。求线性表中第i个元素GetElem(L,i,e)。其作用是返回线性表L的第i个数据元素。通常线性表List的基本运算如下:2.1线性表的基本概念5/133按值查找Locate(L,x)。若L中存在一个或多个值与x相等的元素,则其作用是返回第一个值为x的元素的逻辑序号。插入元素InsElem(L,x,i)。其作用是在线性表L的第i个位置上增加一个以x为值的新元素,删除元素DelElem(L,i)。其作用是删除线性表L的第i个元素ai。输出元素值DispList(L)。其作用是按前后次序输出线性表L的所有元素值。2.1线性表的基本概念6/133线性表抽象数据类型List=线性表中元素的逻辑结构+基本运算定义2.1线性表的基本概念7/133
【例2.1】利用线性表List的基本运算,设计一个在线性表A中删除线性表B中出现的元素的算法。
解:算法思路:依次检查线性表B中的每个元素x,看它是否在线性表A中。若x在线性表A中,则将其从A中删除。2.1线性表的基本概念8/133voidDelete(List&A,ListB) //A为引用型参数{inti,k;ElemTypex;for(i=1;i<=GetLength(B);i++){x=GetElem(B,i);//依次获取线性表B中的元素,存放在x中
k=Locate(A,x);
//在线性表A中查找x
if(k>0)DelElem(A,k); //若在线性表A中找到了,将其删除
}}线性表的基本运算算法2.1线性表的基本概念9/133
【例2.2】利用线性表List的基本运算,设计一个由线性表A和B中的公共元素产生线性表C的算法。
算法思路:先初始化线性表C,然后依次检查线性表A中的每个元素x,看它是否在线性表B中;若x在线性表B中,则将其插入到线性表C中。2.1线性表的基本概念10/133voidCommelem(ListA,ListB,List&C) //C为引用型参数{inti,k,j=1;ElemTypex;
InitList(C);
for(i=1;i<=GetLength(A);i++)
{x=GetElem(A,i); //依次获取线性表A中的元素,存放在x中k=Locate(B,x); //在线性表B中查找x
if(k>0)
{InsElem(C,x,j);
j++; //若在线性表B中找到了,将其插入到C中
}
}}2.1线性表的基本概念11/1332.2顺序表2.2.1顺序表的定义顺序表是线性表采用顺序存储结构在计算机内存中的存储方式,它由多个连续的存储单元构成,每个存储单元存放线性表的一个元素。顺序表逻辑上相邻的数据元素在内存的存储结构中也是相邻的,不需要额外的内存空间来存放元素之间的逻辑关系。2.2顺序表12/133假定线性表的数据元素的类型为ElemType,在C/C++语言中,顺序表类型声明如下:#defineMaxSize100typedefintElemType;
//假设顺序表中所有元素为int类型typedefstruct{
ElemTypedata[MaxSize]; //存放顺序表的元素
intlength; //顺序表的实际长度}SqList; //顺序表类型2.2顺序表13/133顺序表的示意图如下:
由于顺序表采用数组存放元素,而数组具有随机存取特性,所以顺序表具有随机存取特性。元素a1a2…an…下标01n-1MaxSize-1空闲2.2顺序表14/1332.2.2线性表基本运算在顺序表上的实现1.顺序表的基本运算算法(1)初始化线性表运算算法将顺序表L的length域置为0。voidInitList(SqList&L)
//由于L要回传给实参,所以用引用类型{
L.length=0;}2.2顺序表15/133(2)销毁线性表运算算法
这里顺序表L的内存空间是由系统自动分配的,在不再需要时由系统自动释放其空间。所以对应的函数不含任何语句。voidDestroyList(SqListL){}2.2顺序表16/133(3)求线性表长度运算算法返回顺序表L的length域值。intGetLength(SqListL){
returnL.length;}2.2顺序表17/133(4)求线性表中第i个元素运算算法
在逻辑序号i无效时返回特殊值0(假),有效时返回1(真),并用引用型形参e返回第i个元素的值。intGetElem(SqListL,inti,ElemType&e){if(i<1||i>L.length) //无效的i值返回0
return0;
else
{e=L.data[i-1]; //取元素值并返回1
return1;}}2.2顺序表18/133(5)按值查找运算算法
在顺序表L找第一个值为x的元素,找到后返回其逻辑序号,否则返回0(由于线性表的逻辑序号从1开始,这里用0表示没有找到值为x的元素)。intLocate(SqListL,ElemTypex) {inti=0;
while(i<L.length&&L.data[i]!=x)i++; //查找值为x的第1个元素,查找范围为0~L.length-1
if(i>=L.length)return(0);//未找到返回0
elsereturn(i+1);
//找到后返回其逻辑序号}2.2顺序表19/133(6)插入元素运算算法将新元素x插入到顺序表L中逻辑序号为i的位置(如果插入成功,元素x成为线性表的第i个元素)。当i无效时返回0(表示插入失败)。有效时将L.data[i-1..L.length-1]后移一个位置,在L.data[i-1]处插入x,顺序表长度增1,并返回1(表示插入成功。2.2顺序表20/133元素a1…an…下标0…
i-1
…
n-1均后移一个位置ai…元素a1…an…下标0…
i-1
i
…
nai…x插入元素x2.2顺序表21/133intInsElem(SqList&L,ElemTypex,inti){intj;
if(i<1||i>L.length+1||L.length==MaxSize)return0; //无效的参数ifor(j=L.length;j>=i;j--)//将位置为i的结点及之后的结点后移L.data[j]=L.data[j-1];L.data[i-1]=x; //在位置i处放入x
L.length++; //线性表长度增1
return1;}2.2顺序表22/133算法分析当i=n+1时(插入尾元素),移动次数为0,呈现最好的情况。当i=1时(插入第一个元素),移动次数为n,呈现最坏的情况。2.2顺序表23/133平均情况分析a1a2aian……n+1种插入情况在位置i插入新元素x,需要将ai~an的元素均后移一次,移动次数为n-i+1。假设在等概率下pi(pi=1/(n+1)),移动元素的平均次数为:2.2顺序表24/133插入算法的主要时间花费在元素移动上,所以算法InsElem()的平均时间复杂度为O(n)。2.2顺序表25/133(7)删除元素运算算法
删除顺序表L中逻辑序号为i的元素。在i无效时返回0(表示删除失败)。有效时将L.data[i..length-1]前移一个位置,顺序表长度减1,并返回1(表示删除成功。2.2顺序表26/133均前移一个位置元素a1…an…下标0…
i-1i
…
n-1ai+1…ai元素a1…an…下标0…
i-1…
n-2ai+1…删除元素ai2.2顺序表27/133intDelElem(SqList&L,inti){intj;
if(i<1||i>L.length) //无效的参数ireturn0;
for(j=i;j<L.length;j++) //将位置为i的结点之后的结点前移L.data[j-1]=L.data[j];
L.length--; //线性表长度减1
return1;}2.2顺序表28/133算法分析当i=n时(删除尾元素),移动次数为0,呈现最好的情况。当i=1时(删除第一个元素),移动次数为n-1,呈现最坏的情况。2.2顺序表29/133平均情况分析a1a2aian……n种删除情况删除位置i的元素ai,需要将ai+1~an的元素均前移一次,移动次数为n-(i+1)+1=n-i。假设在等概率下pi(pi=1/n),移动元素的平均次数为:2.2顺序表30/133删除算法的主要时间花费在元素移动上,所以算法DelElem()的平均时间复杂度为O(n)。2.2顺序表31/133(8)输出元素值运算算法
从头到尾遍历顺序表L,输出各元素值。voidDispList(SqListL){inti;
for(i=0;i<L.length;i++) printf("%d",L.data[i]);
printf("\n");}2.2顺序表32/133将顺序表类型声明及其基本运算函数存放在SqList.cpp文件中#include"SqList.cpp" //包括前面的顺序表基本运算函数intmain(){inti;ElemTypee;SqListL; //定义一个顺序表L
InitList(L); //初始化顺序表L
InsElem(L,1,1); //插入元素1
InsElem(L,3,2); //插入元素3
InsElem(L,1,3); //插入元素1
InsElem(L,5,4); //插入元素5
InsElem(L,4,5); //插入元素4
InsElem(L,2,6); //插入元素22.2顺序表33/133printf("线性表:");DispList(L);printf("长度:%d\n",GetLength(L));i=3;GetElem(L,i,e);printf("第%d个元素:%d\n",i,e);e=1;printf("元素%d是第%d个元素\n",e,Locate(L,e));i=4;printf("删除第%d个元素\n",i);
DelElem(L,i);printf("线性表:");DispList(L);
DestroyList(L);}线性表:131542长度:6第3个元素:1元素1是第1个元素删除第4个元素线性表:131422.2顺序表34/1332.整体创建顺序表的算法假设给定一个含有n个元素的数组a,由它来创建顺序表。voidCreateList(SqList&L,ElemTypea[],intn){inti,k=0; //k累计顺序表L中的元素个数for(i=0;i<n;i++){L.data[k]=a[i]; //向L中添加一个元素k++; //L中元素个数增1}L.length=k; //设置L的长度}2.2顺序表35/1332.2.3顺序表的算法设计示例1.基于顺序表基本操作的算法设计这类算法设计中包括顺序表元素的查找、插入和删除等。2.2顺序表36/133
【例2.3】假设有一个顺序表L,其中元素为整数且所有元素值均不相同。设计一个算法将最大值元素与最小值元素交换。用maxi和mini记录顺序表L中最大值元素和最小值元素的下标,初始时maxi=mini=0。i从1开始扫描所有元素:当L.data[i]>L.data[maxi]时置maxi=i;否则若L.data[i]<L.data[mini]时置mini=i。扫描完毕时,L.data[maxi]为最大值元素,L.data[mini]为最小值元素,将它们交换。算法思路2.2顺序表37/133voidswap(ElemType&x,ElemType&y) //交换x和y{ElemTypetmp=x;x=y;y=tmp;}voidSwapmaxmin(SqList&L) //交换L中最大值元素与最小值元素{inti,maxi,mini;maxi=mini=0;for(i=1;i<L.length;i++){if(L.data[i]>L.data[maxi])maxi=i;elseif(L.data[i]<L.data[mini])mini=i;}swap(L.data[maxi],L.data[mini]);}2.2顺序表38/133
【例2.4】设计一个算法,从线性表中删除自第i个元素开始的k个元素,其中线性表用顺序表L存储。将线性表中ai~ai+k-1元素(对应L.data[i-1..i+k-2]的元素)删除,即将ai+k~an(对应L.data[i+k-1..n-1])的所有元素依次前移k个位置。2.2顺序表39/133均前移k个位置元素a1…an下标0…
i-1…
i+k-2i+k-1…
n-1ai+k…ai…ai+k-12.2顺序表40/133intDeletek(SqList&L,inti,intk){intj;
if(i<1||k<1||i+k-1>L.length)return0;
//判断i和k是否合法
for(j=i+k-1;j<L.length;j++) //将元素前移k个位置L.data[j-k]=L.data[j];
L.length-=k;
//L的长度减kreturn1;}2.2顺序表41/133
【例2.5】已知线性表(a1,a2,…,an)采用顺序表L存储,且每个元素都是互不相等的整数。设计一个将所有奇数移到所有的偶数前边的算法(要求时间最少,辅助空间最少)。
设计思路:置i=0,j=n-1,在顺序表L中从左向右找到偶数L.data[i],从右向左找到奇数L.data[j],将两者交换;循环这个过程直到i等于j为止。2.2顺序表42/133voidMove(SqList&L){inti=0,j=L.length-1;while(i<j)
{while(L.data[i]%2==1)i++; //从前向后找偶数
while(L.data[j]%2==0)j--; //从后向前找奇数
if(i<j)
swap(L.data[i],L.data[j]);
//交换这两元素}}a1a2a3a4a5a6a7指向偶数:ij:指向奇数交换2.2顺序表43/1332.基于整体建表的算法设计这类算法设计中需要根据条件产生新的结果顺序表。2.2顺序表44/133
【例2.6】已知一个整数线性表采用顺序表L存储。设计一个尽可能高效的算法删除其中所有值为x的元素(假设L中值为x的元素可能有多个)。
由于删除所有x元素后得到的结果顺序表可以与原L共享存储空间,求解问题转化为新建结果顺序表。采用整体创建顺序表的算法思路,将插入元素的条件设置为“不等于x”,即仅仅将不等于x的元素插入到L中。用k记录结果顺序表中元素个数(初始值为0).扫描L,将L中所有不为x的元素重新插入到L中,每插入一个元素k增加1.最后置L的长度为k。2.2顺序表45/133voidDeletex(SqList&L,ElemTypex){inti,k=0;for(i=0;i<L.length;i++){if(L.data[i]!=x) //将不为x的元素插入到L中{L.data[k]=L.data[i];k++;}}L.length=k; //重置L的长度}算法的时间复杂度为O(n),空间复杂度为O(1),属于高效的算法。2.2顺序表46/133
【例2.7】
已知一个整数线性表采用顺序表L存储。设计一个尽可能高效的算法删除其中所有值为负整数的元素(假设L中值为负整数的元素可能有多个)。采用整体创建顺序表的算法思路,仅仅将插入元素的条件设置为“元素值≥0”即可。2.2顺序表47/133voidDeleteminus(SqList&L){inti,k=0;for(i=0;i<L.length;i++){if(L.data[i]>=0) //将不为负数的元素插入到L中{L.data[k]=L.data[i];k++;}}L.length=k; //重置L的长度}2.2顺序表48/1333.有序顺序表的二路归并算法有序表是指按元素值递增或者递减排列的线性表。有序顺序表是有序表的顺序存储结构。也可以采用链式存储结构。对于有序表可以利用其元素的有序性提高相关算法的效率。二路归并就是有序表的一种经典算法。2.2顺序表49/133
【例2.8】已知有两个按元素值递增有序的顺序表A和B(这样的顺序表称递增有序顺序表)。设计一个算法将顺序表A和B的全部元素归并到一个按元素递增有序的顺序表C中。并分析算法的空间复杂度和时间复杂度。
设计思路:用i遍历顺序表A,用j遍历顺序表B。当A和B都未遍历完时,比较两者的当前元素,则将较小者复制到C中,若两者的当前元素相等,则将这两个元素都复制到C中。最后将尚未遍历完的顺序表的余下元素均复制到顺序表C中。这一过程称为二路归并。2.2顺序表50/133例如,A=(1,3,5),B=(2,4,6,8)其二路归并过程如下:
A:135
B:2468ij较小者复制到CC:1234568二路归并过程两个有序表合并成一个有序表的高效算法2.2顺序表51/133voidMerge(SqListA,SqListB,SqList&C) //C为引用型参数{inti=0,j=0,k=0;
//k记录顺序表C中的元素个数
while(i<A.length&&j<B.length)
{if(A.data[i]<B.data[j])
{C.data[k]=A.data[i];
i++;k++;
}
else //A.data[i]>B.data[j]
{C.data[k]=B.data[j];
j++;k++;
}
}2.2顺序表52/133
while(i<A.length)
//将A中剩余的元素复制到C中
{ C.data[k]=A.data[i]; i++;k++;
}
while(j<B.length)
//将B中剩余的元素复制到C中
{ C.data[k]=B.data[j];
j++;k++;
}
C.length=k; //指定顺序表C的实际长度}本算法的空间复杂度为O(1),时间复杂度为O(m+n),其中m和n分别为顺序表A和B的长度。2.2顺序表53/133
【例2.9】已知有两个递增有序顺序表A和B,设计一个算法由顺序表A和B的所有公共元素产生一个顺序表C。并分析该算法的空间复杂度和时间复杂度。
采用二路归并的思路,用i、j分别遍历有序顺序表A、B,跳过不相等的元素,将两者相等的元素(即公共元素)放置到顺序表C中。2.2顺序表54/133voidCommelem(SqListA,SqListB,SqList&C){inti=0,j=0,k=0; //k记录顺序表C中的元素个数
while(i<A.length&&j<B.length)
{if(A.data[i]<B.data[j])
i++;
elseif(A.data[i]>B.data[j])
j++;
else
//A.data[i]=B.data[j]
{C.data[k]=A.data[i];
i++;j++;k++;
}
}
C.length=k; //指定顺序表C的实际长度}本算法的空间复杂度为O(1),时间复杂度为O(m+n)。2.2顺序表55/133
【例2.10】有两个递增有序顺序表A和B,分别含有m和n个整数元素(最大的元素不超过32767),假设这m+n个元素均不相同。
设计一个尽可能高效的算法求这m+n个元素中第k小的元素。如果参数k错误,算法返回0;否则算法返回1,并且用参数e表示求出的第k小的元素。2.2顺序表56/133intTopk1(SqListA,SqListB,intk,ElemType&e){inti=0,j=0;if(k<1||k>A.length+B.length)return0; //参数错误返回0while(i<A.length&&j<B.length){k--;if(A.data[i]<B.data[j]){if(k==0){e=A.data[i];return1;}i++;}2.2顺序表采用二路归并的思路,仅仅找第k次归并的较小元素。57/133else{if(k==0){e=B.data[j];return1;}j++;}} //endwhileif(i<A.length) //A没有扫描完毕e=A.data[i+k-1];elseif(j<B.length) //B没有扫描完毕e=B.data[j+k-1];return1;}2.2顺序表58/133改进#defineINF32767intTopk2(SqListA,SqListB,intk,ElemType&e){inti=0,j=0;if(k<1||k>A.length+B.length)return0; //参数错误返回02.2顺序表59/133while(true){k--;intx=(i<A.length?A.data[i]:INF);inty=(j<B.length?B.data[j]:INF);if(x<y){if(k==0){e=x;return1;}i++;}else{if(k==0){e=y;return1;}j++;}}}2.2顺序表60/1332.3单链表和循环单链表2.3.1单链表的定义
线性表的单链表存储方法:用一个指针表示结点间的逻辑关系。因此单链表的一个存储结点包含两个部分,结点的形式如下:datanextdata:为数据域,用于存储线性表的一个数据元素,也就是说在单链表中一个结点存放一个数据元素。next:为指针域或链域,用于存放一个指针,该指针指向后继元素对应的结点,也就是说单链表中结点的指针用于表示后继关系。2.3单链表和循环单链表61/133单链表分为带头结点和不带头结点两种类型。在许多情况下,带头结点的单链表能够简化运算的实现过程。因此这里讨论的单链表除特别指出外均指带头结点的单链表。2.3单链表和循环单链表62/133仍假设数据元素的类型为ElemType。单链表的结点类型声明如下:typedefstructnode{ElemTypedata;
//数据域
structnode*next;
//指针域}SLinkNode;
//单链表结点类型2.3单链表和循环单链表63/133
在单链表中尾结点之后不再有任何结点,那么它的next域设置为什么值呢?有两种方式:将尾结点的next域用一个特殊值NULL(空指针,不指向任何结点,只起标志作用)表示,这样的单链表为非循环单链表,通常所说的单链表都是指这种类型的单链表。将尾结点的next域指向头结点,这样可以通过尾结点移动到头结点,从而构成一个查找环,将这样的单链表为循环单链表。2.3单链表和循环单链表64/1332.3.2线性表基本运算在单链表上的实现带头结点的单链表:a1a2an∧…Ldatanext不含实际值头结点尾结点2.3单链表和循环单链表65/1331.单链表的基本运算算法(1)初始化线性表运算算法创建一个空的单链表,它只有一个头结点,由L指向它。该结点的next域为空,data域未设定任何值。对应的算法如下:voidInitList(SLinkNode*&L)
//L为引用型参数{L=(SLinkNode*)malloc(sizeof(SLinkNode));
//创建头结点L
L->next=NULL;}2.3单链表和循环单链表66/133(2)销毁线性表运算算法一个单链表中的所有结点空间都是通过malloc函数分配的,在不再需要时需通过free函数释放所有结点的空间。a1a2an∧…Lprep2.3单链表和循环单链表67/133voidDestroyList(SLinkNode*&L){SLinkNode*pre=L,*p=pre->next;
while(p!=NULL)
{free(pre);
pre=p;p=p->next; //pre、p同步后移
}
free(pre);}2.3单链表和循环单链表68/133(3)求线性表的长度运算算法设置一个整型变量i作为计数器,i初值为0,p初始时指向第一个数据结点。然后沿next域逐个往后查找,每移动一次,i值增1。当p所指结点为空时,结束这个过程,i之值即为表长。intGetLength(SLinkNode*L){
inti=0;
SLinkNode*p=L->next;
while(p!=NULL)
{i++;
p=p->next;
}
returni;}2.3单链表和循环单链表69/133(4)求线性表中第i个元素运算算法用p从头开始遍历单链表L中的结点,用计数器j累计遍历过的结点,其初值为0。在遍历时j等于i时,若p不为空,则p所指结点即为要找的结点,查找成功,算法返回1。否则算法返回0表示未找到这样的结点。2.3单链表和循环单链表70/133intGetElem(SLinkNode*L,inti,ElemType&e){intj=0;
SLinkNode*p=L; //p指向头结点,计数器j置为0
if(i<=0)return0; //参数i错误返回0
while(p!=NULL&&j<i)
{j++;
p=p->next;
}
if(p==NULL)return0; //未找到返回0
else
{e=p->data;
return1; //找到后返回1
}}2.3单链表和循环单链表71/133(5)按值查找运算算法在单链表L中从第一个数据结点开始查找第一个值域与e相等的结点,若存在这样的结点,则返回其逻辑序号;否则返回0。2.3单链表和循环单链表72/133intLocate(SLinkNode*L,ElemTypee) {SLinkNode*p=L->next;
intj=1; //p指向第一个数据结点,j置为其序号1
while(p!=NULL&&p->data!=e)
{ p=p->next; j++;
}
if(p==NULL)return(0); //未找到返回0
elsereturn(j); //找到后返回其序号}2.3单链表和循环单链表73/133(6)插入元素运算算法在单链表L中插入第i个值为x的结点。先在单链表L中查找第i-1个结点,若未找到返回0;找到后由p指向该结点,创建一个以x为值的新结点s,将其插入到p指结点之后。2.3单链表和循环单链表74/133插入操作:在p结点之后插入s结点的操作如下:
注意:插入操作的①和②执行顺序不能颠倒。①结点s的next域指向p的下一个结点(s->next=p->next)。②将结点p的next域改为指向新结点s(p->next=s)。*p*sx①②2.3单链表和循环单链表75/133intInsElem(SLinkNode*&L,ElemTypex,inti) {intj=0;
SLinkNode*p=L,*s;if(i<=0)return0;
//参数i错误返回0
while(p!=NULL&&j<i-1)
//查找第i-1个结点p
{ j++; p=p->next;
}
if(p==NULL)return0;
//未找到第i-1个结点时返回0
else
//找到第i-1个结点p
{ s=(SLinkNode*)malloc(sizeof(SLinkNode)); s->data=x;
//创建存放元素x的新结点s s->next=p->next;
//将s结点插入到p结点之后
p->next=s; return1; //插入运算成功,返回1
}}2.3单链表和循环单链表76/133(7)删除元素运算算法先在单链表L中查找第i-1个结点,若未找到返回0。找到后由p指向该结点,然后让q指向后继结点(即要删除的结点)。若q所指结点为空则返回0,否则删除q结点并释放其占用的空间。在单链表L中删除第i个结点。2.3单链表和循环单链表77/133删除p指结点的后继结点的过程:
删除操作:p->next=q->next;free(q);*p**q2.3单链表和循环单链表78/133intDelElem(SLinkNode*&L,inti){intj=0;
SLinkNode*p=L,*q;if(i<=0)return0;
//参数i错误返回0
while(p!=NULL&&j<i-1) //查找第i-1个结点
{ j++; p=p->next;
}
if(p==NULL)return0;
//未找到第i-1个结点时返回0
else
//找到第i-1个结点p
{ q=p->next;
//q指向被删结点
if(q==NULL)return0;//没有第i个结点时返回0 else
{p->next=q->next;
//从单链表中删除q结点
free(q); //释放其空间
return1; }
}}2.3单链表和循环单链表79/133(8)输出线性表运算算法从第一个数据结点开始,沿next域逐个往下遍历,输出每个遍历到结点的data域,直到尾结点为止。voidDispList(SLinkNode*L){SLinkNode*p=L->next;
while(p!=NULL){ printf("%d",p->data); p=p->next;
}
printf("\n");}2.3单链表和循环单链表80/133可以通过调用基本运算算法来创建单链表,其过程是先初始化一个单链表,然后向其中一个一个地插入元素。这里介绍是快速创建整个单链表的算法,也称为整体建表。假设给定一个含有n个元素的数组a,由它来创建单链表,这种建立单链表的常用方法有两种。2.整体创建单链表的算法2.3单链表和循环单链表81/133(1)头插法建表从一个空单链表(含有一个L指向的头结点)开始。读取数组a(含有n个元素)中的一个元素,生成一个新结点s,将读取的数据元素存放到新结点的数据域中。将新结点s插入到当前链表的表头上。再读取数组a的下一个元素,采用相同的操作建立新结点s并插入到单链表L中,直到数组a中所有元素读完为止。2.3单链表和循环单链表82/133voidCreateListF(SLinkNode*&L,ElemTypea[],intn){SLinkNode*s;inti;
L=(SLinkNode*)malloc(sizeof(SLinkNode));//创建头结点L->next=NULL; //头结点的next域置空,表示一个空单链表
for(i=0;i<n;i++) //遍历a数组所有元素
{ s=(SLinkNode*)malloc(sizeof(SLinkNode)); s->data=a[i]; //创建存放a[i]元素的新结点s s->next=L->next; //将s插在头结点之后
L->next=s;
}}2.3单链表和循环单链表83/133若数组a包含元素1、2、3和4,则调用CreateListF(L,a,4)建立的单链表如下图所示。从中看到,单链表L中数据结点的次序与数组a的元素次序正好相反。L4321∧2.3单链表和循环单链表84/133(2)尾插法建表从一个空单链表(含有一个L指向的头结点)开始。读取数组a(含有n个元素)中的一个元素,生成一个新结点s,将读取的数据元素存放到新结点的数据域中。将新结点s插入到当前链表的表尾上。再读取数组a的下一个元素,采用相同的操作建立新结点s并插入到单链表L中,直到数组a中所有元素读完为止。由于尾插法算法每次将新结点插到当前链表的表尾上,为此增加一个尾指针tc,使其始终指向当前链表的尾结点。2.3单链表和循环单链表85/133voidCreateListR(SLinkNode*&L,ElemTypea[],intn){SLinkNode*s,*tc;inti;
L=(SLinkNode*)malloc(sizeof(SLinkNode));//创建头结点
tc=L; //tc始终指向尾结点,初始时指向头结点
for(i=0;i<n;i++)
{ s=(SLinkNode*)malloc(sizeof(SLinkNode)); s->data=a[i]; //创建存放a[i]元素的新结点s tc->next=s; //将s插入tc之后
tc=s;
}
tc->next=NULL; //尾结点next域置为NULL}2.3单链表和循环单链表86/133若数组a包含元素1、2、3和4,则调用CreateListR(L,a,4)建立的单链表如下图所示.从中看到,单链表L中数据结点的次序与数组a的元素次序相同。L1234∧2.3单链表和循环单链表87/1332.3.3单链表的算法设计示例1.基于单链表基本操作的算法设计这类算法设计中包括单链表结点的查找、插入和删除等。2.3单链表和循环单链表88/133
【例2.11】设计一个算法,通过一趟遍历确定单链表L(至少含两个数据结点)中第一个元素值最大的结点。用p遍历单链表,在遍历时用maxp指向data域值最大的结点(maxp的初值为p)。当单链表遍历完毕,最后返回maxp。2.3单链表和循环单链表89/133SLinkNode*Maxnode(SLinkNode*L){SLinkNode*p=L->next,*maxp=p;
while(p!=NULL) //遍历所有的结点
{if(maxp->data<p->data)
maxp=p; //当p指向更大的结点时,将其赋给maxp
p=p->next; //p沿next域下移一个结点
}
returnmaxp;}2.3单链表和循环单链表90/133
【例2.13】设计一个算法,删除一个单链表L(至少含两个数据结点)中第一个元素值最大的结点。在单链表中删除一个结点先要找到它的前驱结点。以p遍历单链表,pre指向p结点的前驱结点。在遍历时用maxp指向data域值最大的结点,maxpre指向maxp结点的前驱结点。当单链表遍历完毕,通过maxpre结点删除其后的结点,即删除了元素值最大的结点。2.3单链表和循环单链表91/133voidDelmaxnode(SLinkNode*&L){SLinkNode*p=L->next,*pre=L,*maxp=p,*maxpre=pre;
while(p!=NULL)
{if(maxp->data<p->data)
{maxp=p;
maxpre=pre;
}
pre=p; //pre、p同步后移,保证pre始终为p的前驱结点
p=p->next;
}
maxpre->next=maxp->next; //删除maxp结点
free(maxp); //释放maxp结点}2.3单链表和循环单链表92/1332.基于整体建表的算法设计这类算法设计中需要根据条件产生新的结果单链表。而创建结果单链表的方法有头插法和尾插法。2.3单链表和循环单链表93/133
【例2.14】设计一个算法,将一个单链表L(至少含两个数据结点)中所有结点逆置。并分析算法的时间复杂度。先将单链表L拆分成两部分,一部分是只有头结点L的空表,另一部分是由p指向第一个数据结点的单链表。然后遍历p,将p所指结点逐一采用头插法插入到L单链表中,由于头插法的特点是建成的单链表结点次序与插入次序正好相反,从而达到结点逆置的目的。2.3单链表和循环单链表94/133voidReverse(SLinkNode*&L){SLinkNode*p=L->next,*q;
L->next=NULL;
while(p!=NULL) //遍历所有数据结点
{ q=p->next; //q临时保存p结点之后的结点
p->next=L->next; //将结点p插入到头结点之后
L->next=p; p=q;
}}2.3单链表和循环单链表95/1333.有序单链表的二路归并算法有序单链表是有序表的单链表存储结构,同样可以利用有序表元素的有序性提高相关算法的效率。当数据采用单链表存储时,对应的二路归并就是单链表二路归并算法。2.3单链表和循环单链表96/133
【例2.16】设ha和hb分别是两个带头结点的递增有序单链表。设计一个算法,将这两个有序链表的所有数据结点合并成一个递增有序的单链表hc,并分析算法的时间和空间复杂度。
要求hc单链表仍使用原来两个链表的存储空间,不另外占用其他的存储空间,ha和hb两个表中允许有重复的数据结点。2.3单链表和循环单链表97/133本例合并后ha、hb两个单链表都不存在了。用二路归并的思路。用pa遍历ha的数据结点,pb遍历hb的数据结点。将ha头结点用作新单链表hc的头结点,让tc始终指向hc的尾结点(初始时指向hc)。当pa和pb均不为空时循环:比较pa与pb之data域值,将较小者链到pc之后。如此重复直到ha或hb为空,再将余下的链表链接到tc之后。2.3单链表和循环单链表98/133voidMerge(SLinkNode*ha,SLinkNode*hb,SLinkNode*&hc){SLinkNode*pa=ha->next,*pb=hb->next,*tc;
hc=ha;tc=hc;
//将ha的头结点用作hc的头结点
free(hb);
//释放hb的头结点
while(pa!=NULL&&pb!=NULL)
{if(pa->data<pb->data)
{tc->next=pa;tc=pa;
//将pa链接到tc之后
pa=pa->next;
}
else //pa->data>pb->data
{tc->next=pb;tc=pb; //将pb链接到tc之后
pb=pb->next;
}
}
tc->next=NULL;
if(pa!=NULL)tc->next=pa;
//ha单链表还有结点时
if(pb!=NULL)tc->next=pb;
//hb单链表还有结点时}2.3单链表和循环单链表99/1334.单链表的排序在很多情况下,单链表中结点有序时可以提高相应算法的效率。2.3单链表和循环单链表100/133
【例2.18】设计一个完整的程序,根据用户输入的学生人数n(n≥3)及每个学生姓名和成绩建立一个单链表,并按学生成绩递减排序,然后按名次输出所有学生的姓名和成绩。解:依题意,声明学生单链表结点类型为StudList:typedefstructnode{charname[10]; //姓名
intscore; //成绩域
structnode*next; //指针域}StudList; //学生单链表结点类型2.3单链表和循环单链表101/133例如,有4个学生记录,其姓名和成绩分别为:(Mary,75),(John,90),(Smith,85),(Harry,95),其构建的带头结点的单链表如下图所示。LMary∧75John90Smith85Harry952.3单链表和循环单链表102/133设计基本运算算法如下:voidCreateStudent(StudList*&sl):采用交互式方式创建学生单链表。voidDestroyList(StudList*&L):销毁学生单链表。voidDispList(StudList*L):输出学生单链表。voidSortList(StudList*&L):将学生单链表按成绩递减排序。2.3单链表和循环单链表103/133采用尾插法创建学生单链表的算法如下:voidCreateStudent(StudList*&sl) //采用尾插法创建学生单链表{intn,i; StudList*s,*tc;
sl=(StudList*)malloc(sizeof(StudList));
//创建头结点
tc=sl; //tc始终指向尾结点,开始时指向头结点
printf("学生人数:");
scanf("%d",&n);
for(i=0;i<n;i++)
{ s=(StudList*)malloc(sizeof(StudList));//创建新结点
printf("第%d个学生姓名和成绩:",i+1); scanf("%s",s->name); //输入姓名和成绩
scanf("%d",&s->score); tc->next=s; //将s插入tc之后
tc=s;
}
tc->next=NULL; //尾结点next域置为NULL}2.3单链表和循环单链表104/133销毁学生单链表的算法如下:voidDestroyList(StudList*&L)//销毁学生单链表{StudList*pre=L,*p=pre->next;
while(p!=NULL)
{ free(pre); pre=p;p=p->next; //pre、p同步后移
}
free(pre);}2.3单链表和循环单链表105/133输出学生单链表的算法如下:voidDispList(StudList*L) //输出学生单链表{StudList*p=L->next;
inti=1;
printf("名次姓名成绩\n");
while(p!=NULL)
{ printf("%d\t\t",i++); printf("%s\t\t",p->name); printf("%d\n",p->score); p=p->next;
}}2.3单链表和循环单链表106/133学生单链表按成绩递减排序的算法设计:LMary∧∧75John90Smith85Harry95p断开成两个部分将p结点有序插入到L中:在L中从前向后查找第一个score小于等于p->score的结点的前驱结点pre。将p结点插入在pre结点之后。直到p=NULL。2.3单链表和循环单链表107/133学生单链表按成绩递减排序的算法如下:voidSortList(StudList*&L) //将学生单链表按成绩递减排序{StudList*p,*pre,*q;
p=L->next->next; //p指向L的第2个数据结点
L->next->next=NULL; //构造只含一个数据结点的有序表while(p!=NULL)
{ q=p->next; //q保存p结点后继结点的指针
pre=L;
//从有序表开头进行比较,pre指向插入p的前驱结点
while(pre->next!=NULL&&pre->next->score>p->score)
pre=pre->next; //在有序表中找插入p的前驱结点pre p->next=pre->next; //将pre之后插入p pre->next=p; p=q; //扫描原单链表余下的结点
}}2.3单链表和循环单链表108/133最后设计如下主函数:intmain(){StudList*st;
printf("(1)建立学生单链表\n");
CreateStudent(st);
printf("(2)按成绩递减排序\n");
SortList(st);
printf("(3)排序后的结果\n");DispList(st);
printf("(4)销毁学生单链表\n");DestroyList(st);}2.3单链表和循环单链表109/133本程序的一次执行结果如下(下划线部分表示用户输入,↙表示回车键):(1)建立学生单链表学生人数:4↙
第1个学生姓名和成绩:Mary75↙
第2个学生姓名和成绩:John90↙
第3个学生姓名和成绩:Smith85↙
第4个学生姓名和成绩:Harry95↙(2)按成绩递减排序(3)排序后的结果名次
姓名成绩
1Harry952John903Smith854Mary75(4)销毁学生单链表2.3单链表和循环单链表110/1332.3.4循环单链表循环单链表的特点是表中尾结点的next域指向头结点,整个链表形成一个环。在循环链表中,从任一结点出发都可以找到表中其他结点。循环单链表L中,p所指结点为尾结点的条件是:p->next==L。带头结点的循环单链表:a1a2an…Ldatanext头结点尾结点2.3单链表和循环单链表111/133(1)初始化线性表运算算法创建一个空的循环单链表,它只有头结点,由L指向它。该结点的next域指向该头结点,data域未设定任何值。voidInitList(SLinkNode*&L)
//L为引用型参数{L=(SLinkNode*)malloc(sizeof(SLinkNode));
L->next=L;}循环单链表的基本运算算法设计。2.3单链表和循环单链表112/133(2)销毁线性表运算算法一个循环单链表中的所有结点空间都是通过malloc函数分配的,在不再需要时需通过free函数释放所有结点的空间。voidDestroyList(SLinkNode*&L){SLinkNode*pre=L,*p=pre->next;
while(p!=L)
{ free(pre); pre=p;p=p->next; //pre、p同步后移
}
free(pre);}2.3单链表和循环单链表113/133(3)求线性表的长度运算算法设置一个整型变量i作为计数器,i初值为0,p初始时指向第一个结点。然后沿next域逐个往下移动,每移动一次,i值增1。当p所指结点为头结点时这一过程结束,i之值即为表长。intGetLength(SLinkNode*L){inti=0;
SLinkNode*p=L->next;
while(p!=L)
{ i++; p=p->next;
}
returni;}2.3单链表和循环单链表114/133(4)求线性表中第i个元素运算算法用p从头开始遍历循环单链表L中的结点(初值指向第一个数据结点),用计数器j累计遍历过的结点,其初值为1。当p不为L且j<i时循环,p后移一个结点,j增1。当循环结束时,若p指向头结点则表示查找失败返回0,否则p所指结点即为要找的结点,查找成功,算法返回1。2.3单链表和循环单链表115/133intGetElem(SLinkNode*L,inti,ElemType&e){intj=1;
SLinkNode*p=L->next; //p指向首结点,计数器j置为1
if(i<=0)return0; //参数i错误返回0
while(p!=L&&j<i) //找第i个结点p
{ j++; p=p->next;
}
if(p==L)return0; //未找到返回0
else
{ e=p->data; return1;
//找到后返回1
}}2.3单链表和循环单链表116/133(5)按值查找运算算法
用i累计查找数据结点的个数,从第一个数据结点开始,由前往后依次比较单链表中各结点数据域的值。若某结点数据域的值等于给定值x,则返回i;否则继续向后比较。若整个单链表中没有这样的结点,则返回0。2.3单链表和循环单链表117/133intLocate(SLinkNode*L,ElemTypex) {
inti=1;
SLinkNod
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 出售转让网店合同样本
- 2024年份3月线上声乐教师虚拟演唱会分成补充协议
- 共享产权房合同样本
- 2025建屋合同(标准版)
- 农村浴室出售合同标准文本
- 农村地基打桩合同样本
- 打造智能社区的未来愿景计划
- 伐木工具租赁合同样本
- 2025合同的订立程序包括哪些步骤
- 农村收购土牛合同样本
- 自考04747Java语言程序设计(一)自学辅导资料
- Unit 3 Section A 3a-3c【 核心精讲+备课精研+高效课堂 】八年级英语下册单元 课件(人教版)
- 三级动火证 模板
- 美术《印象主义-莫奈》教学课件
- 毕业论文-基于单片机的智能浇花系统的设计与实现
- 薪酬管理第6版第9章课件
- XK3168电子称重仪表技术手册
- 电梯系统质量检查记录表
- 最新山东地图含市县地图矢量分层可编辑地图PPT模板
- 机械设计齿轮机构基础
- T∕CGMA 033001-2018 压缩空气站能效分级指南
评论
0/150
提交评论