第2章 线性表 本章主要介绍下列内容 线性表的定义和基本操作 线性表的顺序存储结构 线性表的链式存储结构 线性表的应用举例.

Slides:



Advertisements
Similar presentations
约瑟夫环(递归) 中山职业技术学院 主讲:陈帼鸾 1.
Advertisements

数据结构概论 第2章 线性表 董黎刚 浙江工商大学信电学院
第二章 线性表 £2.4 线性表的应用 £2.1 线性表的类型定义 £2.2 线性表的顺序存储结构 £2.3 线性表的链式存储结构
第二章 线性表 2.1 线性表的逻辑结构 一. 线性表定义如下:
小学生游戏.
数据结构算法 模拟练习及解答二 绍兴文理学院 计算机系计算机应用教研室.
第二章 线性表 ⒈教学内容:2.1 线性表逻辑结构; 2.2 线性表的顺序存储及运算实现; 2.3 线性表的链式存储和实现。
第二章 线性表 1 线性表的逻辑结构及其基本操作 2 线性表的顺序存储结构 3 线性表的链式存储结构 4 静态链表 5 应用实例.
第二章 线性表.
第2章 线性表 2.1 线性表的概念及运算 2.2 线性表的顺序存储 2.3 线性表的链式存储 2.4 一元多项式的表示及相加.
Linked List Operations
單向鏈結串列 Singly Linked Lists.
第2章 线性表 线性结构 是一个数据元素的有序集合。.
其他类型的链表主要内容 静态链表 循环链表 双向链表.
计算机软件技术基础 数据结构与算法(2).
第三章 栈和队列 Stack and Queue
第二章 线性表 线性表 顺序表 链表 顺序表与链表的比较.
数据结构 第2章 线性表 吴忠华.
数 据 结 构 Ch.2 线性表 计 算 机 学 院 肖明军
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
数据结构 第二章 线性表.
制作:崔广才
数据结构 Data Structure 中南大学 主讲人:王国军,郑瑾 中南大学信息院计科系
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
第2章 线性表 2.1 线性表的基本概念 2.2 线性表的顺序存储 2.3 线性表的链式存储 2.4 线性表的应用 2.5 有序表 本章小结.
本 章 说 明 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式存储结构 2.4 循环链表和双向链
西安交通大学计教中心 ctec.xjtu.edu.cn
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第 1 章 数据结构 1.1 基本数据结构与算法 1.2 线性表 1.3 栈和队列 1.4 树和二叉树 1.5 查找 1.6 内部排序 65
第2章 线性表 2.1 线性表 2.2 顺序表 2.3 单链表 2.4 循环单链表 2.5 双向链表 2.6 仿真链表
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
第2章 线性表 丽水学院工学院.
第2章 线性表 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示.
线性表是一种最简单的线性结构 线性结构的基本特征为: 线性结构 是 一个数据元素的有序(次序)集 1.集合中必存在唯一的一个“第一元素”;
第 3 讲 线性表(一).
第3章 栈和队列(一).
第二章 线性表.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
内容回顾 线性表的定义和特点 线性表的顺序存储及查找、插入和删除.
第二章 线性表 2.1 线性表的类型定义 2.2线性表的顺序存储 2.3线性表的链式存储 2.4一元多项式的表示和相加.
(知识点三) 2.3 线性表的链式表示和实现 本节将介绍线性表的另一存储结构—— 链式存储及其对应的操作。
线性表练习.
cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学
顺序表的插入.
王玲 第 2 章 线性表 王玲 2019/2/25.
第二章 线性表.
3.1 线性表及逻辑结构 3.2 线性表的顺序存储 3.3 线性表的链式存储 3.4 链式存储结构的应用
严蔚敏、吴伟民编著 清华大学出版社 学习网站:中国网页设计
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
顺序表的删除.
单链表的基本概念.
顺序查找.
线性结构 线性结构的特点: 线性结构的种类 在数据元素的非空有限集中, (1)存在唯一的一个被称做“第一个”的数据元素;
第 四 讲 线性表(二).
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
第二章 线性表.
第三章 数据组织与处理.
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
3.16 枚举算法及其程序实现 ——数组的作用.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
实验目的:掌握数据的顺序存储结构及它们在计算机中的操作。 实验内容:
第1章 数据结构基础概论 本章主要介绍以下内容 数据结构研究的主要内容 数据结构中涉及的基本概念 算法的概念、描述方法以及评价标准.
第七讲 栈和队列(二) 1/.
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
算法3.3 void InitList_sq(SqList &L,int msize=LIST_INIT_SIZE)
第二章 线性表 线性表是一种最简单的线性结构 线性结构是一个数据元素的有序(次序)集.
第二部分 数据结构—— 用面向对象方法与C++描述.
本节内容 1.二叉排序树的定义 2.二叉排序树的查找操作 3.二叉排序树的插入操作 4.二叉排序树的删除操作 5.二叉排序树的总结
第二章 线性表 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
Presentation transcript:

第2章 线性表 本章主要介绍下列内容 线性表的定义和基本操作 线性表的顺序存储结构 线性表的链式存储结构 线性表的应用举例

2.1 线性表的定义和基本操作 2.2 线性表的顺序存储结构 2.3 线性表的链式存储结构 2.4 线性表的应用举例

2.1 线性表的定义和基本操作 2.1.1 线性表的定义 线性表是由n(n≥0)个类型相同的数据元素组成的有限序列。通常表示成下列形式: 2.1 线性表的定义和基本操作 2.1.1 线性表的定义 线性表是由n(n≥0)个类型相同的数据元素组成的有限序列。通常表示成下列形式: L=( a1, a2,...,ai-1,ai,ai+1,...,an) 其中:L为线性表名称,习惯用大写书写; ai为组成该线性表的数据元素,习惯用小写书写; 线性表中数据元素的个数被称为线性表的长度,当n=0时,线性表为空,又称为空线性表。

举例 La=(34,89,765,12,90,-34,22) 数据元素类型为int。 Ls=(Hello,World, China, Welcome) 数据元素类型为string。 Lb=(book1,book2,...,book100) 数据元素类型为下列所示的结构类型: struct bookinfo{ int No; //图书编号 char *name; //图书名称 char *auther; //作者名称 ...; }

2.1.2 线性表的基本操作 初始化线性表L InitList(L) 销毁线性表L DestoryList(L) 2.1.2 线性表的基本操作 初始化线性表L InitList(L) 销毁线性表L DestoryList(L) 清空线性表L ClearList(L) 求线性表L的长度 ListLength(L) 判断线性表L是否为空 IsEmpty(L) 获取线性表L中的某个数据元素内容 GetElem(L,i,e) 检索值为e的数据元素 LocateELem(L,e) 返回线性表L中e的直接前驱元素 PriorElem(L,e) 返回线性表L中e的直接后继元素 NextElem(L,e) 在线性表L中插入一个数据元素 ListInsert(L,i,e) 删除线性表L中第i个数据元素 ListDelete(L,i,e)

2.2 线性表的顺序存储结构 2.2.1 线性表的顺序存储结构 2.2 线性表的顺序存储结构 2.2.1 线性表的顺序存储结构 线性表的顺序存储结构是指用一组连续的存储单元 依次存储线性表中的每个数据元素。如下图2-1所示:

图2-1 线性表顺序存储结构示意图

其中,L为每个数据元素所占据的存储单元数目。 相邻两个数据元素的存储位置计算公式 LOC(ai+1)=LOC(ai)+L 线性表中任意一个数据元素的存储位置的计算公式为: LOC(ai+1)=LOC(a1)+(i-1)*L 顺序存储结构的特点 (1)利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构(物理结构)一致;

(2)在访问线性表时,可以利用上述给出的数学公式,快速地计算出任何一个数据元素的存储地址。因此,我们可以粗略地认为,访问每个数据元素所花费的时间相等。这种存取元素的方法被称为随机存取法,使用这种存取方法的存储结构被称为随机存储结构。 在C语言中,实现线性表的顺序存储结构的类型定义 #define LIST_MAX_LENGTH 100 //线性表的最大长度 typedef struct { EntryType *item; //指向存放线性表中数据元素的基地址 int length; //线性表的当前长度 }SQ_LIST;

2.2.2 典型操作的算法实现 1. 初始化线性表L int InitList(SQ_LIST *L) { 2.2.2 典型操作的算法实现 1. 初始化线性表L int InitList(SQ_LIST *L) { L->item=(EntryType*)malloc(LIST_MAX_LENGTH *sizeof(EntryType)); //分配空间 if (L->item==NULL) return ERROR; //若分配空间不成功,返回ERROR L->length=0; //将当前线性表长度置0 return OK; //成功返回OK }

2. 销毁线性表L void DestroyList(SQ_LIST *L) { if (L->item) free(L->item); //释放线性表占据的所有存储空间 } 3. 清空线性表L void ClearList(SQ_LIST *L) L->length=0; //将线性表的长度置为0

4. 求线性表L的长度 int GetLength(SQ_LIST L) { return (L.length); } 5. 判断线性表L是否为空 int IsEmpty(SQ_LIST L) if (L.length==0) return TRUE; else return FALSE;

6. 获取线性表L中的某个数据元素的内容 int GetElem(SQ_LIST L,int i,EntryType *e) { if (i<1||i>L.length) return ERROR; //判断i值是否合理,若不合理,返回ERROR *e=L.item[i-1]; //数组中第i-1的单元存储着线性表中第i个数据元素的内容 return OK; }

7. 在线性表L中检索值为e的数据元素 int LocateELem(SQ_LIST L,EntryType e) { for (i=0;i< L.length;i++) if (L.item[i]==e) return i+1; return 0; }

8. 在线性表L中第i个数据元素之前插入数据元素e int ListInsert(SQ_LIST *L,int i,EntryType e) { if (L->length==LIST_MAX_LENGTH) return ERROR; //检查是否有剩余空间 if (i<1||i>L->length+1) return ERROR; //检查i值是否合理 for (j=L->length-1;j>=i-1;i++) //将线性表第i个元素之后的所有元素向后移动 L.->item[j+1]=L->item[j]; L->item[i-1]=e; //将新元素的内容放入线性表的第i个位置, L->length++; return OK; }

9. 将线性表L中第i个数据元素删除 int ListDelete(SQ_LIST *L,int i,EntryType *e) { if (IsEmpty(L)) return ERROR; //检测线性表是否空 if (i<1||i>L->length) return ERROR; //检查i值是否合理 *e=L->item[i-1]; //将欲删除的数据元素内容保留在e所指示的存储单元中 for (j=i;j<=L->length-1;j++) //将线性表第i+1个元素之后的所有元素向前移动 L->item[j-1]=L->item[j]; L->length--; return OK; }

插入算法的分析 假设线性表中含有n个数据元素,在进行插入操作时,若假定在n+1个位置上插入元素的可能性均等,则平均移动元素的个数为:

删除算法的分析 在进行删除操作时,若假定删除每个元素的可能性均等,则平均移动元素的个数为: 分析结论 顺序存储结构表示的线性表,在做插入或删除操作时,平均需要移动大约一半的数据元素。当线性表的数据元素量较大,并且经常要对其做插入或删除操作时,这一点需要值得考虑。

2.3 线性表的链式存储结构 线性表顺序存储结构的特点 2.3 线性表的链式存储结构 线性表顺序存储结构的特点 它是一种简单、方便的存储方式。它要求线性表的数据元素依次存放在连续的存储单元中,从而利用数据元素的存储顺序表示相应的逻辑顺序,这种存储方式属于静态存储形式。 暴露的问题 l 在做插入或删除元素的操作时,会产生大量的数据元素移动; l 对于长度变化较大的线性表,要一次性地分配足够的存储空间,但这些空间常常又得不到充分的利用; l 线性表的容量难以扩充。

线性表的链式存储结构 线性表的链式存储结构是指用一组任意的存储单元(可以连续,也可以不连续)存储线性表中的数据元素。为了反映数据元素之间的逻辑关系,对于每个数据元素不仅要表示它的具体内容,还要附加一个表示它的直接后继元素存储位置的信息。假设有一个线性表(a,b,c,d),可用下图2-2所示的形式存储:

图2-2 线性表链式存储结构示意图

d ^ c b a head 术语 表示每个数据元素的两部分信息组合在一起被称为结点; 其中表示数据元素内容的部分被称为数据域(data); 表示直接后继元素存储地址的部分被称为指针或指针域(next)。 单链表简化的图2-3描述形式 head d ^ c b a 图 2-3 单联表结构示意图

其中,head是头指针,它指向单链表中的第一个结点,这是单链表操作的入口点。由于最后一个结点没有直接后继结点,所以,它的指针域放入一个特殊的值NULL。NULL值在图示中常用(^)符号表示。 带头结点的单链表 为了简化对链表的操作,人们经常在链表的第一个结点之前附加一个结点,并称为头结点。这样可以免去对链表第一个结点的特殊处理。如下图2-4所示: head d ^ c b a 图 2-4 带头结点的单链表结构示意图

链式存储结构的特点 (1)线性表中的数据元素在存储单元中的存放顺序与逻辑顺序不一定一致; (2)在对线性表操作时,只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,这样就会造成寻找第一个结点和寻找最后一个结点所花费的时间不等,具有这种特点的存取方式被称为顺序存取方式。

在C语言中,实现线性表的链式存储结构的类型定义 typedef strcut node{ //结点类型 EntryType item; struct node *next; }NODE; typedef struct{ //链表类型 NODE *head; }LINK_LIST;

2.3.2 典型操作的算法实现 1. 初始化链表L int InitList(LINK_LIST *L) { L->head=(*NODE)malloc(sizeof(NODE)); //为头结点分配存储单元 if (L->head) {L->head->next=NULL; return OK;} else return ERROR ; }

2. 销毁链表L void DestoryList(LINK_LIST *L) { NODE *p; while (L->head){ //依次删除链表中的所有结点 p=L->head; L->head=L->head->next; free(p); }

3. 清空链表L void ClearList(LINK_LIST *L) { NODE *p; while (L->head->next){ p=L->head->next; //p指向链表中头结点后面的第一个结点 L->head->next=p->next; //删除p结点 free(p); //释放p结点占据的存储空间 }

4. 求链表L的长度 int ListLength(LINK_LIST L) { NODE *p; int len; for(p=L.head, len=0;p->next==NULL; p=p->next,len++); return(len); }

5. 判链表L空否。 int IsEmpty(LINK_LIST L) { if (L.head->next==NULL) return TRUE; else return FALSE; }

6. 通过e返回链表L中第i个数据元素的内容 void GetElem(LINK_LIST L,int i,EntryType *e) { NODE *p; int j; if (i<1||i>ListLength(L)) exit ERROR; //检测i值的合理性 for (p=L.head,j=0; j!=i;p=p->next,j++); //找到第i个结点 *e=p->item; //将第i个结点的内容赋给e指针所指向的存储单元中 }

7. 在链表L中检索值为e的数据元素 NODE *LocateELem(LINK_LIST L,EntryType e) { NODE *p; for (p=L.head->next;p&&p->item!=e;p=p->next); //寻找满足条件的结点 return(p); }

8. 返回链表L中结点e的直接前驱结点 NODE *PriorElem(LINK_LIST L,NODE* e) { NODE *p; if (L.head->next==e) return NULL; //检测第一个结点 for (p=L.head;p->next&&p->next!=e;p=p->next); if (p->next==e) return p; esle return NULL; }

9. 返回链表L中结点e的直接后继结点 NODE *NextElem(LINK_LIST L,NODE* e) { NODE *p; for(p=L.head->next;p&&p!=e;p=p->next); if (p) p=p->next; return p; }

10. 在链表L中第i个数据元素之前插入数据元素e int ListInsert(LINK_LIST *L,int i,EntryType e) { NODE *p,*s; int j; if (i<1||i>ListLength(L)+1) return ERROR; s=(NODE*)malloc(sizeof(NODE)); if (s==NULL) return ERROR; s->item=e; for (p=L->head,j=0;p&&j<i-1;p=p->next;j++); //寻找第i-1个结点 s->next=p->next; p->next=s; //将s结点插入 return OK; }

11. 将链表L中第i个数据元素删除,并将其内容保存在e中。 int ListDelete(LINK_LIST *L,int i,EntryType *e) { NODE *p*s; int j; if (i<1||i>ListLength(L)) return ERROR; //检查i值的合理性 for(p=L->head, j=0;j<i-1;p=p->next,j++); //寻找第i-1个结点 s=p->next; //用s指向将要删除的结点 *e=s->item; p->next=s->next; //删除s指针所指向的结点 free(s); return OK; }

head 2.3.3 循环链表 若将链表中最后一个结点的next域指向 实现循环链表的类型定义与单链表完全相同,它 的所有操作也都与单链表类似。只是判断链表结束的 条件有所不同。下面我们就列举两个循环链表操作的 算法示例。 head 图2-7 带头结点的循环链表示意图

1. 初始化链表CL int InitList(LINK_LIST *CL) { CL->head=(*NODE)malloc(sizeof(NODE)); if (CL->head) {CL->head->next=CL->head; return OK;} //让next域指向它自身 else return ERROR ; }

2. 在循环链表CL中检索值为e的数据元素 NODE *LocateELem(LINK_LIST CL,EntryType e) { NODE *p; for (p=CL.head->next;(p!=CL.head)&&(p->item!=e);p=p->next); if (p!=CL.head) return p; else return NULL ; }

2.3.4 双向循环链表 在循环链表中,访问结点的特点 访问后继结点,只需要向后走一步,而访问前驱结点,就需要转一圈。 2.3.4 双向循环链表 在循环链表中,访问结点的特点 访问后继结点,只需要向后走一步,而访问前驱结点,就需要转一圈。 结论:循环链表并不适用于经常访问前驱结点的情况。 解决方法:在需要频繁地同时访问前驱和后继结点的时候,使用双向链表。 双向链表就是每个结点有两个指针域。一个指向后继结点,另一个指向前驱结点。

prior item next (a) head (b) 图 2-8

用C语言实现双向循环链表的类型定义—— typedef strcut du_node{ //双向链表的结点类型 EntryType item; struct du_node *prior,*next; }DU_NODE; typedef struct{ //双向链表类型 DU_NODE *head; }DU_LINK_LIST;

(1)初始化双向循环链表DL int InitDuList(DU_LINK_LIST *DL) { DL->head=(DU_NODE*)malloc(sizeof(DU_NODE)); //为头结点分配存储单元 if (DL->head==NULL) return ERROR; DL->head->next=DL->head; //让头结点的next域指向自身 DL->head->prior=DL->head; //让头结点的prior域指向自身 return OK; }

(2)在双向循环链表DL中,第i个数据元素之前 插入数据元素e 在一个结点之前插入一个新结点的过程。 在双向循环链表中的p结点之前插入s结点应使用下列语句序列: s->next=p; s->prior=p->prior; p->prior->next=s; p->prior=s;

p s 图 2-9

完整的算法: int DuListInsert(DU_LINK_LIST *L,int i,EntryType e) { DU_NODE *p,*s; int j; if (i<1||i>ListLength(DL)+1) return ERROR; //检测i值的合理性 s=(DU_NODE*)malloc(sizeof(DU_NODE)); //为新结点分配存储单元 if (s==NULL) return ERROR; s->item=e; for (p=L->head,j=0;p&&j<i;p=p->next;j++); //寻找第i 个结点 s->next=p; s->prior=p->prior; //将新结点插入 p->prior->next=s; p->prior=s; return OK; }

(3)创建双向循环链表DL。 void Create_Du_Link_List(DU_LINK_LIST *DL) { if (InitDulist(DL)==ERROR) exit ERROR; scanf(“%d”,&data); for (int i=1;data;i++){ DuListInsert(DL,i,data); }

2.4 线性表的应用举例 约瑟夫(Joseph)问题:编号为1,2,···,n的n个人按顺时针方向围坐在一张圆桌旁,每个人手中持有一个密码(正整数)。首先输入一个正整数作为报数上限值m,然后,从第一个人开始按顺时针方向自1开始顺序报数,报到m的人离开桌旁,并将他手中的密码作为新的m值,从顺时针方向的下一个就坐在桌旁的人人开始重新从1报数,如此下去,直至所有人全部离开桌旁为止。

假设有7个人,编号从1到7,他们手中的密码分别是3,1,7,2,4,8,4,最初的m=2,通过报数,这7个人离开桌旁的顺序应该是:2,3,5,4,7,6,1。 数据结构的分析 这个问题的主角是n个人,每个人需要描述的信息 有:编号、密码和是否在桌旁的状态。假设有7个人, 他们的信息可以表示成下面的形式。

顺序存储结构 算法描述 让n个人围坐在一张圆桌旁; for (i=1;i<=n;i++){ 从1开始报数,报到m停止; 报到m的人离开桌子; } 最终的算法实现—— #define LIST_MAX_LENGTH 7 #define n LIST_MAX_LENGTH typedef int EntryType; //将EntryType定义为int类型 void Joseph(int code[ ],int n)

{//通过一维数组code带入n个人手中的密码,n是开始就坐在桌旁的人数 SQ_LIST people; int temp,m; //m是报数的上限值 scanf(“%d”,&m); //输入最初的m if (InitList(&people)==ERROR) exit ERROR; for (i=1;i<=n;i++) if (ListInsert(&people,i,code[i-1])==ERROR) exit ERROR; position=0; //记录当前报数人的编号 count=0; //记录当前所报的数目

for (i=1;i<=n;i++) { do{ //报数 position=(position+1)%n; GetElem(people,position,&temp); if (temp>0) count++; }while (count!=m); printf(“%d”,position); //输出当前离开桌旁人的编号 GetElem(people,position,&m); people.item[position-1]=-people.item[position-1]; //将密码变为负值 }

链式存储结构 使用一个不带头结点的循环单链表结构。结点结构为: No code next 图 2-10

用C语言定义 typedef struct{ //循环链表中每个结点的数据域部分的类型 int No; //编号 int code; //密码 }INFO; typedef INFO EntryType;

算法 void Joseph(int code[],int n) { LINK_LIST people; NODE *position,*pre; //position指向当前报数的结点 if (InitList(&people)==ERROR) exit ERROR; //初始化链表people for (i=1;i<=n;i++) //以n个人的信息为数据域内容向链表插入n个结点 if (ListInsert(&people,i,code[i-1])==ERROR) exit ERROR;

position=people.head; while (position->next!=people.head) position= NextElem(people,position); scanf(“%d”,&m); //输入最初的m for (i=1;i<n;i++){ count=0; //报数处理 do{ position=NextElem(people,position); count++; }while (count!=m);

printf(“%d”,position->item.No); //离开桌子处理 m=position->item.code; pre=PriorElem(people,position); pre->next=position->next; free(position); position= pre; } printf(“%d”,position->item.No); //处理最后一个人