收藏 分享(赏)

算法与数据结构第2章 线性表.ppt

上传人:dzzj200808 文档编号:3350374 上传时间:2018-10-17 格式:PPT 页数:132 大小:664KB
下载 相关 举报
算法与数据结构第2章  线性表.ppt_第1页
第1页 / 共132页
算法与数据结构第2章  线性表.ppt_第2页
第2页 / 共132页
算法与数据结构第2章  线性表.ppt_第3页
第3页 / 共132页
算法与数据结构第2章  线性表.ppt_第4页
第4页 / 共132页
算法与数据结构第2章  线性表.ppt_第5页
第5页 / 共132页
点击查看更多>>
资源描述

1、第2章 线性表,2.1 线性表的基本概念,2.2 线性表的顺序存储,2.3 线性表的链式存储,2.4 线性表的应用,本章小结,2.5 有序表,2.1 线性表的基本概念,2.1.1 线性表的定义,2.1.2 线性表的运算,2.1.1 线性表的定义线性表是具有相同特性的数据元素的一个有限序列。该序列中所含元素的个数叫做线性表的长度,用n表示,n0。当n=0时,表示线性表是一个空表,即表中不包含任何元素。设序列中第i(i表示逻辑位序)个元素为ai(1in)。线性表的一般表示为:(a1,a2,ai,ai+1,an),逻辑结构,其中a1为第一个元素,又称做表头元素,a2为第二个元素,an为最后一个元素,

2、又称做表尾元素。例如,在线性表(1,4,3,2,8,10) 中,1为表头元素,10为表尾元素。,2.1.2 线性表的运算线性表的基本运算如下:(1) 初始化线性表InitList(&L):构造一个空的线性表L。(2) 销毁线性表DestroyList(&L):释放线性表L占用的内存空间。,(3) 判线性表是否为空表ListEmpty(L):若L为空表,则返回真,否则返回假。(4) 求线性表的长度ListLength(L):返回L中元素个数。(5) 输出线性表DispList(L):当线性表L不为空时,顺序显示L中各结点的值域。(6) 求线性表L中指定位置的某个数据元素GetElem(L,i,&

3、e):用e返回L中第 i(1iListLength(L)个元素的值。,(7) 定位查找LocateElem(L,e):返回L中第1个值域与e相等的位序。若这样的元素不存在,则返回值为0。(8) 插入数据元素ListInsert(&L,i,e):在L的第i(1iListLength(L)+1)个元素之前插入新的元素e,L的长度增1。(9) 删除数据元素ListDelete(&L,i,&e):删除L的第i(1iListLength(L)个元素,并用e返回其值,L的长度减1。,例2.1 假设有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即线性表中的数据元素即为集合中的成员。编写一个

4、算法求一个新的集合C=AB,即将两个集合的并集放在线性表LC中。,利用已有基本运算求解问题,解题思路:LC LALC LB中不在LA中的元素,void unionList(List LA,List LB,List ,lenc=0;for (i=1;i=lenb;i+) GetElem(LB,i,e);/取LB中第i个数据元素赋给eif (!LocateElem(LA,e) ListInsert(LC,+lenc,e); /LA中不存在和e相同者,则插入到LC中 ,体现结构化编程的思想。,由于LocateElem(LA,e)运算的时间复杂度为O(ListLength(LA),所以本算法的时间复杂

5、度为:O(ListLength(LA)*ListLength(LB)。,2.2 线性表的顺序存储,2.2.1 线性表的顺序存储顺序表,2.2.2 顺序表基本运算的实现,2.2.1 线性表的顺序存储顺序表线性表的顺序存储结构就是:把线性表中的所有元素按照其逻辑顺序依次存储到从计算机存储器中指定存储位置开始的一块连续的存储空间中。这样,线性表中第一个元素的存储位置就是指定的存储位置,第i+1个元素(1in-1)的存储位置紧接在第i个元素的存储位置的后面。,线性表 逻辑结构 顺序表 存储结构,区别,假定线性表的元素类型为ElemType,则每个元素所占用存储空间大小(即字节数)为sizeof(Ele

6、mType),整个线性表所占用存储空间的大小为: n*sizeof(ElemType)其中,n表示线性表的长度。,顺序表示意图,在定义一个线性表的顺序存储类型时,需要定义一个数组来存储线线表中的所有元素和定义一个整型变量来存储线性表的长度。假定数组用dataMaxSize表示,长度整型变量用length表示,并采用结构体类型表示,则元素类型为通用类型标识符ElemType的线性表的顺序存储类型可描述如下:,typedef struct ElemType dataMaxSize;int length; SqList; /顺序表类型其中,data成员存放元素,length成员存放线性表的实际长度。

7、说明:由于C/C+中数组的下标从0开始,线性表的第i个元素ai存放顺序表的第i-1位置上。为了清楚,将ai在逻辑序列中的位置称为逻辑位序,在顺序表中的位置称为物理位序。,对于第1章的逻辑结构City,假定每个元素占用30个存储单元,数据从100号单元开始由低地址向高地址方向存储,对应的顺序表如下:,2.2.2 顺序表基本运算的实现,一旦采用顺序表存储结构,我们就可以用C/C+语言实现线性表的各种基本运算。为了方便,假设ElemType为char类型,使用如下自定义类型语句:typedef char ElemType;,1. 建立顺序表其方法是将给定的含有n个元素的数组的每个元素依次放入到顺序表

8、中,并将n赋给顺序表的长度成员。算法如下:void CreateList(SqList *,2. 顺序表基本运算算法 (1) 初始化线性表InitList(L)该运算的结果是构造一个空的线性表L。实际上只需将length成员设置为0即可。void InitList(SqList *本算法的时间复杂度为O(1)。,顺序表,L,引用的作用 main() SqList *sq;InitList(sq);op(sq); void InitList (SqList *L) /不用引用 L=(SqList *) malloc (sizeof(SqList);L-length=0; ,?,sq,L,0,?,

9、sq,main:,调用InitList,返回到Main:,引用的作用 main() SqList *sq;InitList(sq);op(sq); void InitList (SqList *&L) /用引用 L=(SqList *) malloc (sizeof(SqList);L-length=0; ,?,sq,L,0,sq,main:,调用InitList,返回到main:,(2) 销毁线性表DestroyList(L)该运算的结果是释放线性表L占用的内存空间。void DestroyList(SqList *本算法的时间复杂度为O(1)。,思考题:这里采用顺序指针,而不是直接给定顺序

10、表。两者有什么区别?,如果直接采用顺序表: void InitList(SqList ,(3) 判定是否为空表ListEmpty(L)该运算返回一个值表示L是否为空表。若L为空表,则返回1,否则返回0。int ListEmpty(SqList *L)return(L-length=0); 本算法的时间复杂度为O(1)。,(4) 求线性表的长度ListLength(L)该运算返回顺序表L的长度。实际上只需返回length成员的值即可。int ListLength(SqList *L)return(L-length);本算法的时间复杂度为O(1)。,(5) 输出线性表DispList(L)该运算当

11、线性表L不为空时,顺序显示L中各元素的值。void DispList(SqList *L) int i;if (ListEmpty(L) return;for (i=0;ilength;i+)printf(“%c“,L-datai);printf(“n“);,本算法中基本运算为for循环中的printf语句,故时间复杂度为:O(L-length)或O(n),(6) 求某个数据元素值GetElem(L,i,e)该运算返回L中第 i(1iListLength(L)个元素的值,存放在e中。int GetElem(SqList *L,int i,ElemType 本算法的时间复杂度为O(1)。,(7)

12、 按元素值查找LocateElem(L,e)该运算顺序查找第1个值域与e相等的元素的位序。若这样的元素不存在,则返回值为0。int LocateElem(SqList *L, ElemType e) int i=0;while (ilength ,本算法中基本运算为while循环中的i+语句,故时间复杂度为:O(L-length)或O(n),(8) 插入数据元素ListInsert(L,i,e)该运算在顺序表L的第i个位置(1iListLength(L)+1)上插入新的元素e。思路:如果i值不正确,则显示相应错误信息;否则将顺序表原来第i个元素及以后元素均后移一个位置,腾出一个空位置插入新元素

13、,顺序表长度增1。,int ListInsert(SqList * ,逻辑位序 1 i i+1 n MaxSize,例如:ListInsert_Sq(L, 5, 66),L-length-1,0,87,56,42,66,i-; /*将顺序表位序转化为data下标*/ for (j=L-length;ji;j-) L-dataj=L-dataj-1;/*将datai及后面元素后移一个位置*/ L-datai=e;,对于本算法来说,元素移动的次数不仅与表长L.length=n有关,而且与插入位置i有关:当i=n+1时,移动次数为0;当i=1时,移动次数为n,达到最大值。在线性表sq中共有n+1个可

14、以插入元素的地方。假设pi(= )是在第i个位置上插入一个元素的概率,则在长度为n的线性表中插入一个元素时所需移动元素的平均次数为:因此插入算法的平均时间复杂度为O(n)。,an-1ai-1(共n-1-(i-1)+1=n-i+1)个元素移动,(9) 删除数据元素ListDelete(L,i,e)删除顺序表L中的第i(1iListLength(L)个元素。思路:如果i值不正确,则显示相应错误信息;否则将线性表第i个元素以后元素均向前移动一个位置,这样覆盖了原来的第i个元素,达到删除该元素的目的,最后顺序表长度减1。,int ListDelete(SqList * ,逻辑位序 1 i i+1 n

15、MaxSize,L-length-1,0,87,56,i-; /将顺序表位序转化为data下标 e=L-datai; for(j=i;jlength-1;j+) L-dataj=L-dataj+1;,例如:ListDelete_Sq(L, 5, e),对于本算法来说,元素移动的次数也与表长n和删除元素的位置i有关:当i=n时,移动次数为0;当i=1时,移动次数为n-1。在线性表sq中共有n个元素可以被删除。假设pi(pi= )是删除第i个位置上元素的概率,则在长度为n的线性表中删除一个元素时所需移动元素的平均次数为:= 因此删除算法的平均时间复杂度为O(n)。,aian-1(共n-1-i+1=

16、n-i个元素)均前移一个位置,例2.2 设计一个算法,将x插入到一个有序(从小到大排序)的线性表(顺序存储结构即顺序表)的适当位置上,并保持线性表的有序性。解:先通过比较在顺序表L中找到存放x的位置i,然后将x插入到L-datai中,最后将顺序表的长度增1。,void Insert(SqList * ,查找插入位置,元素后移一个位置,逻辑位序 1 i i+1 n MaxSize,例2.3 设计一个算法,将两个元素有序(从小到大)的顺序表合并成一个有序顺序表。 求解思路:将两个顺序表进行二路归并。,i,j,i,j,1,k,1 2 3,k,i,j,1 2 3 4,k,i,1 2 3 4 5,k,j

17、,j,i,1 2 3 4 5,k,复制,SqList *merge(SqList *p, SqList *q) SqList *r; int i=0,j=0,k=0;r=(SqList *)malloc(sizeof(SqList);while (ilength ,while (ilength) r-datak=p-datai;i+;k+; while (jlength) r-datak=q-dataj;j+;k+; r-length=k; /或p-length+q-lengthreturn(r); ,例2.4 已知长度为n的线性表A采用顺序存储结构,编写一个时间复杂度为O(n)、空间复杂度为

18、O(1)的算法,该算法删除线性表中所有值为item的数据元素。解:用k记录顺序表A中等于item的元素个数,边扫描A边统计k,并将不为item的元素前移k个位置,最后修改A的长度。对应的算法如下:,void delnode1(SqList /顺序表A的长度等于k ,算法1:类似于建顺序表,void delnode2(SqList /顺序表A的长度递减 ,算法2,上述算法中只有一个while循环,时间复杂度为O(n)。算法中只用了i,k两个临时变量,空间复杂度为O(1)。,例2.5 设顺序表有10个元素,其元素类型为整型。设计一个算法,以第一个元素为分界线,将所有小于它的元素移到该元素的前面,将

19、所有大于它的元素移到该元素的后面。,while(i!=j) while(ji ,2.3 线性表的链式存储,2.3.1 线性表的链式存储链表,2.3.2 单链表基本运算的实现,2.3.3 双链表,2.3.4 循环链表,2.3.5 静态链表,2.3.1 线性表的链式存储链表在链式存储中,每个存储结点不仅包含有所存元素本身的信息(称之为数据域),而且包含有元素之间逻辑关系的信息,即前驱结点包含有后继结点的地址信息,这称为指针域,这样可以通过前驱结点的指针域方便地找到后继结点的位置,提高数据查找速度。一般地,每个结点有一个或多个这样的指针域。若一个结点中的某个指针域不需要任何结点,则仅它的值为空,用常

20、量NULL表示。,由于顺序表中的每个元素至多只有一个前驱元素和一个后继元素,即数据元素之间是一对一的逻辑关系,所以当进行链式存储时,一种最简单也最常用的方法是:在每个结点中除包含有数据域外,只设置一个指针域,用以指向其后继结点,这样构成的链接表称为线性单向链接表,简称单链表。,带头结点单链表示意图,在线性表的链式存储中,为了便于插入和删除算法的实现,每个链表带有一个头结点,并通过头结点的指针惟一标识该链表。,对于第1章的逻辑结构City,采用带头结点的单链表存储时的结构如下所示:,在单链表中,由于每个结点只包含有一个指向后继结点的指针,所以当访问过一个结点后,只能接着访问它的后继结点,而无法访

21、问它的前驱结点。,另一种可以采用的方法是:在每个结点中除包含有数值域外,设置有两个指针域,分别用以指向其前驱结点和后继结点,这样构成的链接表称之为线性双向链接表,简称双链表。,带头结点的双链表示意图,在双链表中,由于每个结点既包含有一个指向后继结点的指针,又包含有一个指向前驱结点的指针,所以当访问过一个结点后,既可以依次向后访问每一个结点,也可以依次向前访问每一个结点。,双链表的特点,在单链表中,假定每个结点类型用LinkList表示,它应包括存储元素的数据域,这里用data表示,其类型用通用类型标识符ElemType表示,还包括存储后继元素位置的指针域,这里用next表示。 LinkList

22、类型的定义如下:typedef struct LNode /定义单链表结点类型 ElemType data;struct LNode *next; /指向后继结点 LinkList;,2.3.2 单链表基本运算的实现,1. 建立单链表先考虑如何建立单链表。假设我们通过一个含有n个数据的数组来建立单链表。建立单链表的常用方法有如下两种:(1) 头插法建表该方法从一个空表开始,读取字符数组a中的字符,生成新结点,将读取的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到结束为止。采用头插法建表的算法如下:,void CreateListF(LinkList* ,i=0,i=1,i

23、=2,i=3,L,采用头插法建立单链表的过程,L,L,L,L,第1步:建头结点,第2步:i0,新建a结点,插入到头结点之后,第3步:i1,新建d结点,插入到头结点之后,第4步:i2,新建c结点,插入到头结点之后,第5步:i3,新建b结点,插入到头结点之后,(2) 尾插法建表头插法建立链表虽然算法简单,但生成的链表中结点的次序和原数组元素的顺序相反。若希望两者次序一致,可采用尾插法建立。该方法是将新结点插到当前链表的表尾上,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点。采用尾插法建表的算法如下:,void CreateListR(LinkList * /终端结点next域置为NULL

24、,b,c,d,a,采用尾插法建立单链表的过程,2. 插入结点运算插入运算是将值为x的新结点插入到单链表的第i个结点的位置上。先在单链表中找到第i-1个结点,再在其后插入新结点。单链表插入结点的过程如下图所示。,插入结点示意图,3. 删除结点运算删除运算是将单链表的第i个结点删去。先在单链表中找到第i-1个结点,再删除其后的结点。删除单链表结点的过程如下图所示。,删除结点示意图,4. 线性表基本运算实现(1) 初始化线性表InitList(L)该运算建立一个空的单链表,即创建一个头结点。void InitList(LinkList *,L,(2) 销毁线性表DestroyList(L)释放单链表

25、L占用的内存空间。即逐一释放全部结点的空间。void DestroyList(LinkList *,初始时,循环结束时,L,p,q,L,p,q=NULL,(3) 判线性表是否为空表ListEmpty(L)若单链表L没有数据结点,则返回真,否则返回假。int ListEmpty(LinkList *L)return(L-next=NULL);,(4) 求线性表的长度ListLength(L)返回单链表L中数据结点的个数。int ListLength(LinkList *L) LinkList *p=L;int i=0;while (p-next!=NULL) i+;p=p-next;return

26、(i);,初始时,循环结束时,L,p,i=0,L,p,i为结点个数,(5) 输出线性表DispList(L)逐一扫描单链表L的每个数据结点,并显示各结点的data域值。void DispList(LinkList *L) LinkList *p=L-next;while (p!=NULL) printf(“%c“,p-data);p=p-next;printf(“n“);,(6)求线性表L中指定位置的某个数据元素GetElem(L,i,&e)思路:在单链表L中从头开始找到第 i个结点,若存在第i个数据结点,则将其data域值赋给变量e。,int GetElem(LinkList *L,int

27、i,ElemType ,循环结束时,L,i,j,(7) 按元素值查找LocateElem(L,e)思路:在单链表L中从头开始找第1个值域与e相等的结点,若存在这样的结点,则返回位置,否则返回0。int LocateElem(LinkList *L,ElemType e) LinkList *p=L-next;int i=1;while (p!=NULL ,循环结束时,L,e,i,(8) 插入数据元素ListInsert(,if (p=NULL) return 0; /未找到位序为i-1的结点else /找到位序为i-1的结点*p s=(LinkList *)malloc(sizeof(Link

28、List);/创建新结点*ss-data=e;s-next=p-next; /将*s插入到*p之后p-next=s;return 1; ,(9) 删除数据元素ListDelete(,if (p=NULL) return 0; /未找到位序为i-1的结点else /找到位序为i-1的结点*p q=p-next; /q指向要删除的结点if (q=NULL) return 0;/若不存在第i个结点,返回0p-next=q-next; /从单链表中删除*q结点free(q); /释放*q结点return 1; ,例2.5 设C=a1,b1,a2,b2,an,bn为一线性表,采用带头结点的hc单链表存放

29、,编写一个算法,将其拆分为两个线性表,使得:A=a1,a2,an,B=b1,b2,bn,解: 设拆分后的两个线性表都用带头结点的单链表存放。先建立两个头结点*ha和*hb,它们用于存放拆分后的线性表A和B,ra和rb分别指向这两个单链表的表尾,用p指针扫描单链表hc,将当前结点*p链到ha未尾,p沿next域下移一个结点,若不为空,则当前结点*p链到hb未尾,p沿next域下移一个结点,如此这样,直到p为空。最后将两个尾结点的next域置空。,ha,p,hb,void fun(LinkList*hc, LinkList* /rb始终指向hb的末尾结点,while (p!=NULL) ra-ne

30、xt=p;ra=p; /将*p链到ha单链表未尾p=p-next;rb-next=p;rb=p; /将*p链到hb单链表未尾p=p-next;ra-next=rb-next=NULL; /两个尾结点的next域置空 ,本算法实际上是采用尾插法建立两个新表。所以,尾插法建表算法是很多类似习题的基础!,思考题 已知一个带有表头结点的单链表,结点结构为:,假设该单链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,算法输出该结点的data域的值,并返回1;否则,只返回0。要求:(1)描述算法的基本设计思想;(2)描

31、述算法的详细实现步骤;(3)根据设计思想和实现步骤,采用程序设计语言描述算法(使用C或C+或JAVA语言实现),关键之处请给出简要注释。,09考研题,关键:找正数第n-k+1个结点。,(1)算法的基本设计思想:(5分)定义两个指针变量p和q,初始时均指向头结点的下一个结点。p指针沿链表移动;当p指针移动到第k个结点时,q指针开始与p指针同步移动;当p指针移动到链表最后一个结点时,q指针所指元素为倒数第k个结点。 以上过程对链表仅进行一遍扫描。(2)算法的详细实现步骤:(5分)count=0,p和q指向链表表头结点的下一个结点;若p为空,转;若count等于k,则q指向下一个结点;否则, cou

32、nt=count+1;p指向下一个结点,转;若count等于k,则查找成功,输出该结点的data域的值,返回1;否则,查找失败,返回0;算法结束。,(3)算法实现:(5分) typedef struct LNode int data;struct LNode *link; *LinkList; int Searchk(LinkList list,int k) LinkList p,q;int count=0;p=q=list-link;while (p!=NULL) if (countlink;p=p-link;if (countdata);return(1); ,【评分说明】(1)若所结出的

33、算法采用一遍扫描方式就能得到正确结果,可给满分15分;若采用两遍或多遍扫描才能得到正确结果的,最高分10分。若采用递归算法得到正确结果的,最高给10分;若实现的算法的空间复杂度过高(使用了大小与k有关的辅助数组),但结果正确,最高给10分。(2)参考答案中只给出了使用C语言的版本,使用C+/JAVA语言正确实现的算法同样给分。(3)若在算法基本思想描述和算法步骤描述中因文字表达没有非常清晰地反映出算法的思考,但在算法实现中能够清晰看出算法思路和步骤且正确,按照(1)的标准给分。(4)若考生的答案中算法算法基本思想描述、算法步骤描述或算法实现中部分正确,可酌情给分。,例2.6 有一个带头结点的单

34、链表head,其ElemType类型为char,设计一个算法使其元素递增有序。解:若原单链表中有一个或以上的数据结点,先构造只含一个数据结点的有序表(只含一个数据结点的单链表一定是有序表)。扫描原单链表余下的结点*p(直到p=NULL为止),在有序表中通过比较找插入*p的前驱结点*q,然后将*p插入到*q之后(这里实际上采用的是直接插入排序方法)。,head,p,void Sort(LinkList * /r保存*p结点后继结点的指针,q=head;while (q-next!=NULL /扫描原单链表余下的结点 ,2.3.3 双链表 对于双链表,采用类似于单链表的类型定义,其DLinkLis

35、t类型的定义如下:typedef struct DNode /定义双链表结点类型 ElemType data;struct DNode *prior; /指向前驱结点struct DNode *next; /指向后继结点 DLinkList;,在双链表中,有些操作如求长度、取元素值和查找元素等操作算法与单链表中相应算法是相同的,这里不多讨论。但在单链表中,进行结点插入和删除时涉及到前后结点的一个指针域的变化。而在双链表中,结点的插入和删除操作涉及到前后结点的两个指针域的变化。,思考题:单链表和双链表有什么不同?,双链表中插入结点示意图,归纳起来,在双链表中p所指的结点之后插入一个*s结点。其操

36、作语句描述为:s-next=p-next;/将*s插入到*p之后p-next-prior=s;s-prior=p;p-next=s;,删除结点示意图,在双链表中删除一个结点的过程如右图所示:,归纳起来,删除双链表L中*p结点的后续结点。其操作语句描述为:p-next=q-next;q-next-prior=p;,2.3.4 循环链表循环链表是另一种形式的链式存储结构。它的特点是表中最后一个结点的指针域不再是空,而是指向表头结点,整个链表形成一个环。由此,从表中任一结点出发均可找到链表中其他结点。,带头结点的循环单链表和循环双链表的示意图,思考题:循环链表和非循环链表有什么不同?,例2.7 编写

37、出判断带头结点的双向循环链表L是否对称相等的算法。解:p从左向右扫描L,q从右向左扫描L,若对应数据结点的data域不相等,则退出循环,否则继续比较,直到p与q相等或p的下一个结点为*q为止。对应算法如下:,int Equeal(DLinkList *L) int same=1;DLinkList *p=L-next; /p指向第一个数据结点DLinkList *q=L-prior; /q指向最后数据结点while (same=1)if (p-data!=q-data) same=0;else if (p=q) break; /数据结点为奇数的情况q=q-prior;if (p=q) brea

38、k; /数据结点为偶数的情况p=p-next;return same; ,2.3.5 静态链表静态链表借用一维数组来描述线性链表。数组中的一个分量表示一个结点,同时使用游标(指示器cur即为伪指针)代替指针以指示结点在数组中的相对位置。数组中的第0个分量可以看成头结点,其指针域指示静态链表的第一个结点。这种存储结构仍然需要预先分配一个较大空间,但是在进行线性表的插入和删除操作时不需要移动元素,仅需要修改“指针”,因此仍然具有链式存储结构的主要优点。,下图给出了一个静态链表的示例。图(a)是一个修改之前的静态链表,图(b)是删除数据元素“陈华”之后的静态链表,图(c)插入数据元素“王华”之后的静

39、态链表,图中用阴影表示修改的游标。,思考题:静态链表有什么特点?,2.4 线性表的应用,问题描述计算任意两个表的简单自然连接过程讨论线性表的应用。假设有两个表A和B,分别是m1行、n1列和m2行、n2列,它们简单自然连接结果C=A B,其中i表示表A中列号,j表示表B中的列号,C为A和B的笛卡儿积中满足指定连接条件的所有记录组,该连接条件为表A的第i列与表B的第j列相等。例如:,i=j,C=A B的计算结果如下:,3=1,数据组织,注意:头结点和数据结点的类型不同!,由于每个表的行数不确定,为此,用单链表作为表的存储结构,每行作为一个数据结点。另外,每行中的数据个数也是不确定的,但由于提供随机

40、查找行中的数据,所以每行的数据采用顺序存储结构,这里用长度为MaxCol的数组存储每行的数据。因此,该单链表中数据结点类型定义如下:#define MaxCol 10 /最大列数typedef struct Node1 /定义数据结点类型 ElemType dataMaxCol;struct Node1 *next; /指向后继数据结点 DList;,另外,需要指定每个表的行数和列数,为此将单链表的头结点类型定义如下:typedef struct Node2 /定义头结点类型 int Row,Col; /行数和列数DList *next; /指向第一个数据结点 HList;采用尾插法建表方法创

41、建单链表,用户先输入表的行数和列数,然后输入各行的数据,为了简便,假设表中数据为int型,因此定义:typedef int ElemType;,顺序表和链表混合使用!,设计运算算法 create(HList *&h):交互式创建多项式单链表。 display(HList *h):输出单链表。 link(HList *h1,HList *h2,HList *&h):实现两个多项式单链表的连接运算。,void create(HList * ,采用尾插法建表,对应的输出表的算法如下: void display(HList *h) int j;DList *p=h-next;while (p!=NUL

42、L) for (j=0;jCol;j+)printf(“%4d“,p-dataj);printf(“n“);p=p-next; ,为了实现两个表h1和h2的简单自然连接,先要输入两个表连接的列序号f1和f2,然后扫描单链表h1,对于h1的每个结点,从头至尾扫描单链表h2,若自然连接条件成立,即h1的当前结点*p和h2的当前结点*q满足:p-dataf1-1=q-dataf2-1 则在新建单链表h中添加一个新结点。新建的单链表h也是采用尾插法建表方法创建的。实现两个表h1和h2的简单自然连接并生成结果h的算法如下:,void link(HList *h1,HList *h2,HList *,wh

43、ile (q!=NULL) if (p-dataf1-1=q-dataf2-1) /对应字段值相等 s=(DList *)malloc(sizeof(DList); /创建一个数据结点for (i=0;iCol;i+) /复制表1的当前行s-datai=p-datai;for (i=0;iCol;i+) /复制表2的当前行s-datah1-Col+i=q-datai;if (h-next=NULL) h-next=s;else r-next=s;r=s; /r始终指向最后数据结点h-Row+; /表行数增1q=q-next; /表2下移一个记录p=p-next; /表1下移一个记录r-next

44、=NULL;/表尾结点next域置空,尾插法建表,设计求解程序建立如下主函数调用上述算法:void main() HList *h1,*h2,*h;printf(“表1:n“); CreateTable(h1); /创建表1printf(“表2:n“); CreateTable(h2); /创建表2LinkTable(h1,h2,h); /连接两个表printf(“连接结果表:n“); DispTable(h); /输出连接结果,运行结果表1: 表的行数,列数:3 3第1行:1 2 3第2行:2 3 3第3行:1 1 1 表2: 表的行数,列数:3 2第1行:3 5第2行:1 6第3行:3 4

45、 连接字段是:第1个表位序,第2个表位序:3 1 连接结果表: 1 2 3 3 5 1 2 3 3 4 2 3 3 3 5 2 3 3 3 4 1 1 1 1 6,思考题:体会数据结构中求解问题的一般过程。,2.5 有序表,所谓有序表,是指这样的线性表,其中所有元素以递增或递减方式排列,并规定有序表中不存在元素值相同的元素。在这里仍以顺序表进行存储。,其中只有ListInsert()基本运算与前面的顺序表对应的运算有所差异,其余都是相同的。有序表的ListInsert()运算对应的算法如下:,思考题:有序表和线性表有什么异同?有序表和顺序表有什么不同?,int ListInsert(SqList ,注意:这里用的不是顺序表指针,直接就是顺序表,本章小结 本章的基本学习要点如下:(1) 理解线性表的逻辑结构特性。(2) 深入掌握线性表的两种存储方法,即顺序表和链表。体会这两种存储结构之间的差异。(3) 重点掌握顺序表和链表上各种基本运算的实现。(4) 综合运用线性表这种数据结构解决一些复杂的实际问题。,练习题2p56:习题2、3和4。 上机实验题p56:题6和7。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报