1、上堂课要点回顾,单链表 类型定义 基本操作实现 应用*,数据结构课程内容,第 四 次 课,阅读:朱战立,第36-44页、第323-328页练习:Project1, 链式存储结构的优缺点, 优点(1)无需事先了解线性表的长度,结点空间动态申请和释放,允许线性表的长度有很大的变化(2)插入删除操作无需大量移动元素,只要修改指针;能够适应经常插入删除元素的情况, 缺点(1)链表的操作算法较复杂(2)指针域需额外占用空间(3)非随机存储结构,需从头指针遍历查 找结点, 例2-7 (P43) 线性表的就地排序, 问题描述: 设head是单链表的头指针,编写算法将数据元素按照其值递增有序的顺序进行就地排序
2、。例:head=(8,4,10,7) 则 head=(4,7,8,10),例2-6 自学,辅助空间为O(1)称就地排序,辅助空间为O(n)称非就地排序,思想:通过改变结点间的指针链接关系实现就地排序。从“空表”开始,依次将元素结点逐个插入到链表中。具体如下:1、将head表断开,形成两个表:head空表和p表2、依次从p表中取出第一个结点(q指针),将q结点移动到head表中合适位置处:2.1、 在head表中根据数据域大小从头指针开始寻找q结点的插入位置i(应该找到第i-1个结点才便于删除,因此定义curr指针寻找第i个结点,pre指针始终指向curr结点的直接前驱结点);2.2、 从p表中
3、删除q结点;2.3、 将q结点插在head表pre结点之后(curr结点之前); 循环第2步直至p表为空,【单链表就地排序算法】,void LinListSort(SLNode *head) /head为带头结点的单链表,就地排序破坏原来的表。 SLNode *curr,*pre,*p,*q;/curr和pre:搜索指针 p=head-next;/p表head-next=NULL;/head表置为空表while (p!=NULL) curr=head-next;/curr初始化为指向首元结点pre=head;/pre始终指向curr结点的直接前驱while( curr!=NULL ,时间复杂度
4、O(n2),2.3.5 循环单链表(简称循环链表), 定义:在单链表的尾结点的链域中放入指向头结点的指针,使整个链表形成一个环形。 逻辑状态:,满足head=head-next,空表:,非空表:, 优点:不增加额外开销开销,却给不少操作带来方便,从循环表中任一结点出发都能访问到表中其它结点循环链表的运算与单链表的基本一致, 差别仅在于算法中的循环条件,不是p或 p-next是否为空,而是它们是否等于head。,2.3.6 双向链表, 结点结构弥补单链表next域仅指向后继结点,不能有效找到前驱的不足,增加一个指向直接前驱的指针。, 结点描述类型 typedef struct Node Data
5、Type data;struct Node *prior,*next; DLNode; /*双向链表结点结构*/, 逻辑状态带头结点的双向链表,非空表:,空表:, 逻辑状态带头结点的双向循环链表,非空表:,空表:, 双向链表中结点的一个重要特征,显然, ppriornext = = pnextprior = = p,p, 插入一个结点的实现,x,pprior=s;,sprior=pprior;,ppriornext=s;,snext=p;, 删除一个结点的实现,pnextprior=pprior;,pproirnext = pnext;,free(p);,几种主要链表比较,单链表:,循环单链表
6、:,非空表:,双向链表:,双向循环链表:,应用场合的选择,不宜使用顺序表的场合 线性表的最大长度不确定时 经常插入删除时 不宜使用链表的场合 当读操作比插入删除操作频率大时 当指针存储开销和整个结点内容所占空间相比,其比例较大时,2.4 静态链表,在一维数组中增加指针域用来存放下一个数据元素在数组中的下标,从而构成用数组描述的链表。因为数组内存空间的申请方式是静态的,所以称为静态链表,增加的指针称做仿真指针。便于在没有指针类型的程序设计语言中使用链表。静态链表结构如下:,JesephRing问题模拟,【问题描述】已知n个人(编号分别为1,2,.,n)按顺时针方向围坐一圈,每人持有一个正整数密码
7、。 开始时任选一个报数上限值m,从编号为1的人按顺时针方向自1开始报数,报到m时停止报数,报m的那个人出列;将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,数到m的那个人又出列;依此规律重复下去,直到所有人全部出列。输入n,m两个正整数,设计程序来求出列的序列。【例如】n = 7, 7个人的密码分别为3,1,7,2,4,8,4。初始报数上限值m=20。 【解答】出局人的顺序为6, 1, 4, 7, 2, 3, 5,ADT JesephRing数据元素:ai=(ni,mi),i=0,1,n-1 (n0),ni,mi代表第i个人的编号和持有的密码 ,其中ni不能省略。结构:是线
8、性结构,对数据元素ai (0in-2)存在次序关系 , a0的直前是an-1 ,an-1的直继是a0。逻辑操作:设J为 JesephRing型JesephRingInitiate(J) /构造一个空的约瑟夫环J。JesephRingInsertEnd(J, x) /*在约瑟夫环J尾部插入新元素x ,成功返回1,失败返回0。*/JesephRing(J,m) /*给定初始报数上限m,从1开始循环报数、删除、输出直至J为空,得到出列序列。*/,问题的抽象数据类型分析,存储结构分析,分析逻辑操作特点和空间比例 频繁插入和删除 指针存储开销和整个结点内容所占空间相比,其比例较小 综上所述,不采用顺序表
9、,而采用链表 经常在尾部插入,因此增设尾指针,始终指向尾部结点 a0的直前是an-1 ,an-1的直继是a0 ,因此采用循环单链表 综上所述,采用带尾指针的循环单链表存储结构,目的:有效地组织和处理数据,带尾指针的循环单链表,逻辑状态分析逻辑操作 初始化SCListInitiate(L) 在表尾插入新结点SCListInsertEnd(L, x) 删除p结点的下一个结点SCListDeleteAfter(L,p) 判断是否是空表SCListNotEmpty(L),rear,rear,带尾指针的循环单链表实现 “SCListWithRear.h“,#include “LinkList.h “ t
10、ypedef struct SLNode * head;/头指针SLNode * rear;/尾指针 SCListWithRear; /*带尾指针的单链表类型定义*/,void SCListInitiate(SCListWithRear *L) /初始化带尾指针的循环单链表 ListInitiate(/尾指针处理 ,int SCListNotEmpty(SCListWithRear *L) if(L-head-next=L-head) return 0;else return 1; ,int SCListInsertEnd(SCListWithRear *L,DataType x) /*在带尾
11、指针的循环单链表的末尾结点后插入一个新元素x。*/ SLNode *q; q=(SLNode *)malloc (sizeof(SLNode); /*新结点*/q-data=x; q-next=L-rear-next; /*插入*/L-rear-next=q;L-rear=q;/尾指针处理return 1; ,时间复杂度O(1),int SCListDeleteAfter(SCListWithRear *L,SLNode *p) /*删除并释放带尾指针的循环单链表L中p结点的下一个结点*/ SLNode *s;s=p-next; p-next=p-next-next;/删除p结点的下一个结点i
12、f(s-next=L-head)L-rear=p;/尾指针处理free(s);return 1; ,时间复杂度O(1),算法设计JesephRing.cpp,#include #include typedef struct int number;int cipher; DataType; #include “SCListWithRear.h”,void JesephRingInitiate(SCListWithRear *J) /*构造一个空的约瑟夫环J。*/SCListInitiate(J); int JesephRingInsertEnd(SCListWithRear *J, DataTy
13、pe x) /*在约瑟夫环J尾部插入新元素x */return SCListInsertEnd(J,x); ,JesephRing(J,m)基本算法: 1、根据初始报数上限m值,寻找第m个结点(应该找到第m-1个结点的地址才便于删除,因此为便于删除定义curr指针找第m个结点,pre指针始终指向curr结点的直接前驱结点) 2、输出第m结点的number值 3、把该结点的cipher值赋给m,作为下一次循环的报数上限m 4、删除第m个结点 如此循环n次或直至表为空时结束。(实际只需循环n-1次,最后只剩一个结点时,直接输出即可),void JesephRing(SCListWithRear *
14、J, int m) /给定初始报数上限m,从1开始循环报数、删出、输出直至J为空 SLNode *pre,*curr; int i;pre=J-head; curr=J-head-next;while(SCListNotEmpty(J)=1) for (i=1;inext;if(curr=J-head)/跳过头结点 pre=curr; curr=curr-next; printf(“%d “,curr-data.number);m=curr-data.cipher; curr=curr-next;if(curr=J-head) curr=curr-next; /跳过头结点SCListDelet
15、eAfter(J,pre); ,时间复杂度O(nm),void main() SCListWithRear JR; int n=7,m=20;DataType test7=1,3,2,1,3,7,4,2,5,4, 6,8,7,4; int i;SCListInitiate( ,第一次上机实习题目 实现一元多项式抽象数据类型,一元n次多项式:,ADT Polynomial数据元素:ai=?结构:?逻辑操作:1、构造一个空的多项式2、多项式插入新的一项3、多项式合并同类项4、多项式加法5、多项式乘法6、打印多项式7、计算多项式的值,1. 问题描述和规格说明/需求分析2. 设计(目标:有效组织与处理
16、数据)2.1 抽象数据类型2.2 存储结构设计2.3 算法设计2.4 模块及调用关系3. 使用说明4. 调试分析5. 测试数据6. 源程序清单,实习报告内容,指针的作用(补充内容),SLNode *CreateLinkList(int n) /基本思想:从“空表”的初态开始,依次生成元素结点,并逐个插入到链表中。具体可以从表头表尾逐步建立。 SLNode *l1;SLNode *p; int i;l1=(SLNode *)malloc(sizeof(SLNode);l1-next=null;p=l1;for (i=0;inext=(SLNode *)malloc(sizeof(SLNode);p=p-next; scanf(“%d”, ,建带头结点的单链表(补充),时间复杂度:O(n),