数 据 结 构 Ch.2 线性表 计 算 机 学 院 肖明军 Email: xiaomj@ustc.edu.cn http://staff.ustc.edu.cn/~xiaomj.

Slides:



Advertisements
Similar presentations
数据结构概论 第2章 线性表 董黎刚 浙江工商大学信电学院
Advertisements

第三章 鏈結串列 Linked List.
第二章 线性表 £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章 线性表 吴忠华.
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
线性表 顺序表 单链表 循环链表 双向链表 多项式
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
数据结构 第二章 线性表.
制作:崔广才
数据结构 Data Structure 中南大学 主讲人:王国军,郑瑾 中南大学信息院计科系
Ch.3 栈和队列 黄刘生 中国科学技术大学计算机系 国家高性能计算中心(合肥)
教 师:曾晓东 电 话: 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
第十章 C高级程序应用—链表* 10.1链表的基本概念 10.2单向链表 10.3双向链表 10.4应用举例.
第2章 线性表 2.1 线性表 2.2 顺序表 2.3 单链表 2.4 循环单链表 2.5 双向链表 2.6 仿真链表
第2章 线性表 丽水学院工学院.
第2章 线性表 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示.
线性表是一种最简单的线性结构 线性结构的基本特征为: 线性结构 是 一个数据元素的有序(次序)集 1.集合中必存在唯一的一个“第一元素”;
第 3 讲 线性表(一).
第二章 线性表.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
内容回顾 线性表的定义和特点 线性表的顺序存储及查找、插入和删除.
第二章 线性表 2.1 线性表的类型定义 2.2线性表的顺序存储 2.3线性表的链式存储 2.4一元多项式的表示和相加.
(知识点三) 2.3 线性表的链式表示和实现 本节将介绍线性表的另一存储结构—— 链式存储及其对应的操作。
线性表练习.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学
顺序表的插入.
王玲 第 2 章 线性表 王玲 2019/2/25.
第2章 线性表 本章主要介绍下列内容 线性表的定义和基本操作 线性表的顺序存储结构 线性表的链式存储结构 线性表的应用举例.
3.1 线性表及逻辑结构 3.2 线性表的顺序存储 3.3 线性表的链式存储 3.4 链式存储结构的应用
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
严蔚敏、吴伟民编著 清华大学出版社 学习网站:中国网页设计
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
顺序表的删除.
单链表的基本概念.
顺序查找.
线性结构 线性结构的特点: 线性结构的种类 在数据元素的非空有限集中, (1)存在唯一的一个被称做“第一个”的数据元素;
第 四 讲 线性表(二).
本教學投影片係屬教科書著作之延伸,亦受著作權法之保護。
第二章 线性表.
第三章 数据组织与处理.
3.16 枚举算法及其程序实现 ——数组的作用.
第 六 讲 栈和队列(一).
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
实验目的:掌握数据的顺序存储结构及它们在计算机中的操作。 实验内容:
第三章 线性表 3.1 线性表的类型定义 3.2 顺序存储的线性表 3.3 链式存储的线性表 3.4 有序表 3.5 顺序表和链表的综合比较.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
算法3.3 void InitList_sq(SqList &L,int msize=LIST_INIT_SIZE)
插入排序的正确性证明 以及各种改进方法.
第二章 线性表 线性表是一种最简单的线性结构 线性结构是一个数据元素的有序(次序)集.
第二部分 数据结构—— 用面向对象方法与C++描述.
第二章 线性表 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
Presentation transcript:

数 据 结 构 Ch.2 线性表 计 算 机 学 院 肖明军 Email: xiaomj@ustc.edu.cn http://staff.ustc.edu.cn/~xiaomj

§2.1 线性表的逻辑结构 线性表:由n(n≥0)个结点a1, …, an组成的有限序列 记作:L = (a1, a2, …, an), ai----一般是同一类型

§2.1 线性表的逻辑结构 逻辑特征 当L≠Ф时, 有且仅有1个开始结点a1和1个终端结点an,a1仅有一直接后继,an仅有一直接前驱 内部结点ai(2≤i≤n-1)均有一直接前驱ai-1和一直接后继ai+1 结点之间的逻辑关系是由位置次序决定的,ai出在表的第i个位置。该关系是线性的(全序的),故称L为线性表。

§2.1 线性表的逻辑结构 举例: ai----抽象符号。具体应用可能是一个结构类型的对象 基本运算: 英文字母表(A, B, …, Z) 扑克牌点数(2, 3, … , 10, J, Q, K, A) 学生成绩表 等 ai----抽象符号。具体应用可能是一个结构类型的对象 线性表是一种相当灵活的数据结构,其长度可根据需要增长或缩短 基本运算: InitList(L), ListLength(L), GetNode(L, i) …等 复杂运算可通过基本运算去构造

§Ch.2 线性表 线性表的抽象数据类型定义 ADT List{ 数据对象:D = { ai | ai∈ElemSet, i=1,2,…,n, n≧0 } 数据关系:R = {<ai-1, ai> | ai-1, ai∈D, i=2,3,…,n } 基本操作: InitList( &L ) 操作结果:构造一个空的线性表L; ListLength( L ) 初始条件:线性表L已存在; 操作结果:若L为空表,则返回TRUE,否则返回FALSE;

§Ch.2 线性表 … } ADT List …. GetElem( L, i, &e ) 初始条件:线性表L已存在,1≦i≦ListLength(L); 操作结果:用e返回L中第i个数据元素的值; ListInsert ( L, i, &e ) 初始条件:线性表L已存在,1≦i≦ListLength(L) ; 操作结果:在线性表L中的第i个位置插入元素e; … } ADT List

§2.2 线性表的顺序存储结构 §2.2.1 顺序表 定义:将线性表中结点按逻辑次序依次存储在一组地址连续的存储单元里。由此存储的L称为顺序表。 地址计算: 设结点大小为l个单元,其中第i个单元为该结点地址 Loc(ai) = Loc(a1) + (i-1) * l L的基地址

§2.2.1 顺序表 特征: 随机存储 只需存储结点。无需用辅助空间存储逻辑关系。但空闲大小不易确定,易浪费空间 静态分配(当用向量实施时)亦可动态申请空间,但copy成本大(扩充表时)。

§2.2.1 顺序表 实现:用向量实现 #define ListSize 100; //假定 typedef int DataType; //依具体使用定 typedef struct { DataType data[ListSize]; //存放结点 int length; //当前表长n }Seglist;

§2.2.2 顺序表上实现的基本运算 1.插入 基本思想: 在L的第i个位置上插入一新结点x(1 ≤ i ≤ n+1)。 x (a1, …, ai-1, ai, ai+1, …, an) → (a1, …, ai-1, x, ai, …, an+1) 为保持结点的物理顺序和逻辑顺序一致,须: 将an, …, ai依次后移1位,空出ith位置 插入x 表长加1

§2.2.2 顺序表上实现的基本运算 算法: void InsertList(SegList *L, DataType x, int i){ int j; //注意c数组1st位置的下标为0. ai的位置是data[i-1]. if (i<1 || i>L->length+1) //1≤i≤n+1是合法插入位置 Error(“Position error!”); if (L->length >= ListSize) Error (“Overflow”); //溢出 for (j = L->length-1; j>=i-1; j--) L->data[j+1] = L->data[j]; //结点后移 L->data[i-1] = x; //插入x L->length++; //长度加1 };

§2.2.2 顺序表上实现的基本运算 分析: 循环后移,移动节点数为n-i+1,时间与 相关 最好情况:当i=n+1,不移动 O(1) 常数时间解释 O(n0)与n无关 最坏情况:当i=1,全部后移 O(n) 平均性能: 设任何合法位置上插入的概率相等: (位置i插入的概率) 当位置i确定是,后移次数亦确定:n-i+1. 故表中平均移动结点为: 即插入式,平均移动表中一半结点

§2.2.2 顺序表上实现的基本运算 2.删除 基本思想: 在L中删除ith结点 (1 ≤ i ≤ n)。 为保持物理与逻辑顺序一致,须: (a1, …, ai-1, ai, ai+1, …, an) → (a1, …, ai-1, ai+1, …, an-1) 为保持物理与逻辑顺序一致,须: 前移:将ai+1, …, an依次前移1位,填补删除ai留下的空间 表长减1

§2.2.2 顺序表上实现的基本运算 算法: void DeleteList(SegList *L, int i){ int j, n=L->length; if (i<1 || i>n) Error(“Position error!”); //非法位置 for (j = i; j<=n-1; j++) L->data[j-1] = L->data[j]; //结点后移 L->length--; //长度减1 };

§2.2.2 顺序表上实现的基本运算 分析: 前移次数与n和i相关,移动节点数n-i。 最好情况:当i=n,不移动 O(1) 最坏情况:当i=1,前移n-1次 O(n) 平均性能:

§2.3 线性表的链式存储结构 顺序表 §2.3.1 单链表(线性链表) 存储方法: 缺点:移动节点,不利于动态插入和删除 优点:随机存取,得到第i个结点的时间为O(1)与表长和位置无关 §2.3.1 单链表(线性链表) 存储方法: 用一组任意存储单元来存放结点(可连续,亦可不连续); 为表示逻辑关系,须存储后继或前驱信息

§2.3.1 单链表(线性链表) 结点结构 表示 数据域 指针域(链域) next指向该结点的后继(的地址) 开始结点无前驱,用头指针表示 终端结点无后继,next域为空(NULL)

§2.3.1 单链表(线性链表) 特征 逻辑状态 头指针唯一确定一个单链表 存储状态 见图2.5 非顺序存取 用指针表示结点间逻辑关系 存储状态 见图2.5 特征 非顺序存取 用指针表示结点间逻辑关系 动态分配

§2.3.1 单链表(线性链表) 实现: typedef struct node{ //结点类型定义 DataType data; //数据域 struct node *next; //指针域 }ListNode; typedef ListNode *LinkList; //链表类型定义 ListNode *p; //结点定义 LinkList head; //链表头指针定义

§2.3.1 单链表(线性链表) 结点变量和指针变量 指针变量p:值为结点地址 结点变量*p:值为结点内容 动态申请,垃圾收集

§2.3.1 单链表(线性链表) 1.建立单链表 (1)头插法: 从空表开始,重复读入数据:申请新结点、插入表头,直至输入结束。 表次序与输入次序相反。 处理自然简单

§2.3.1 单链表(线性链表) (2)尾插法: 设为指针r指向当前链尾(初值为NULL) 申请新结点s 将读入数据写入 新结点链到表尾(应注意边界条件) 修改尾指针r

§2.3.1 单链表(线性链表) 为简便起见,设结点数据类型DataType为char,输入字符,换行符结束 LinkList CreateList(void){ //ch, head, r为局部量 head = r = NULL; //开始为空表 while ((ch = getchar()) != ‘\n’){ s = (ListNode*)malloc(sizeof(ListNode)); // ① s->data = ch; // ② if (head == NULL) //插入空表 head = s; //新结点插入空表(边界),r为空

§2.3.1 单链表(线性链表) 边界条件处理: 空表和非空表处理不一致 简化方法:引入头结点(哨兵),其中data域可做它用(如表长度) else r->next = s; // ③ r = s; // ④ } if (r != NULL) r->next = NULL; //非空表,终端结点指针为空无后继 return head; 边界条件处理: 空表和非空表处理不一致 简化方法:引入头结点(哨兵),其中data域可做它用(如表长度)

§2.3.1 单链表(线性链表) LinkList CreateList(void){ //用尾插法建立带头结点的单链表 char ch; LinkList head = (Linklist)malloc(sizeof(ListNode)); //生成头结点 ListNode *s, *r; r = head; //为指针初始指向头结点 while () { s = (ListNode*)malloc(sizeof(ListNode)); s->data = ch;

§2.3.1 单链表(线性链表) Note:为简化起见,,申请结点时不判是否申请到 时间: O(n) r->next = s; r = s; } r->next = NULL; //终端结点指针置空,或空表时头结点指针置空 return head; Note:为简化起见,,申请结点时不判是否申请到 时间: O(n)

§2.3.1 单链表(线性链表) 2.查找 按值查找 找链表中关键字为k的结点 按序号查找 思想: 算法 合法位置 1≤i≤n. 头结点可看作第0个结点 返回第i个结点的地址,即找到第i-1个结点,返回其next域 思想: 顺链扫描:p----当前扫描到的结点,初始指向头结点 j----计算器。累加扫描到的结点,初始值为0 每次加1,直至 j=i为止,p指向ith结点 算法

§2.3.1 单链表(线性链表) 时间分析 循环终止条件是搜索到表尾或j=i。复杂度最多为i,与查找位置相关。 //i=0,找头结点 ListNode *GetNode(LinkList head, int i){ //在链表(有头结点)中查找ith结点 找到(0≤i≤n)则返回该结点的存储 //位置,否则返回NULL int j; ListNode *p; p = head; j = 0; //头结点开始扫描 while (p->next && j<i) {//顺链扫描,至p->next为空或j=i为止 p = p->next; j++; } if (i == j) return p; //找到 else return NULL; //当i<0或i>n时未找到 时间分析 循环终止条件是搜索到表尾或j=i。复杂度最多为i,与查找位置相关。 //i=0,找头结点

§2.3.1 单链表(线性链表) 3.插入 问题:将值为x的结点插到表的第i个结点位置上。即在ai-1和ai之间插入。故须找到第ai-1个结点的地址p,然后生成新结点*s,将其链到ai-1之后,ai之前。

§2.3.1 单链表(线性链表) 思考:若无头结点,边界如何处理? 时间:主要是GetNode O(n) 合法位置 1≤i≤n+1 void InsertList (LinkList head, DataType x, int i) { //带头结点 1≤i≤n+1 ListNode *p; p = GetNode(head, i-1); //寻找第i-1个结点① if (p== NULL) // i<1或i>n+1时插入位置i有错 Error("position error"); s = (ListNode *)malloc(sizeof (ListNode)); //② s->data=x; //③ s->next=p->next; //④ p->next=s; //⑤ } 思考:若无头结点,边界如何处理? 时间:主要是GetNode O(n) 合法位置 1≤i≤n+1 GetNode: 0≤i≤n

§2.3.1 单链表(线性链表) 4.删除 删ith结点。首先找ai-1。

§2.3.1 单链表(线性链表) void DeleteList (LinkList head, int i){ p = GetNode (head, i-1); //① if (!p || !(p->next)) // i<1或i>n时删除位置有错 Error("position error"); r = p->next; //② 令r指向被删结点ai p->next = r->next; // ③ 将ai从链上摘下 free(r); // ④ 释放ai }

§2.3.1 单链表(线性链表) 正确性分析 时间 O(n) 结论:链表上插、删无须移动结点,仅需修改指针 i<1时,GetNode中的参数<0。此时返回p为空 i=n+1时,GetNode中参数=n。返回终端结点,此时p->next为空 i>n+1时,GetNode返回p为空 时间 O(n) 结论:链表上插、删无须移动结点,仅需修改指针

§2.3.2 循环链表(单) 首尾相接:不增加存储量,只修改连接方式 优点:从任一结点出发可访问到表中任一其它结点,如找前驱(O(n)) 遍历表的终止方式: p为空或p->next为空 p=head或p->next=head

§2.3.2 循环链表(单) 仅设尾指针更方便:访问开始结点和终端结点的时间均为O(1)。 T(m,n)=O(max(m,n))或O(min(m,n))

§2.3.3 双链表(循环) 单链表只有后向链,找一结点的后继时间为O(1),但找前驱费时,若经常涉及找前驱和后继,则可选双链表。

§2.3.3 双链表(循环) 结构特征 优点: 对称结构:若p不空,则 p->prior->next = p = p->next->prior 优点: 找一结点前驱和找一结点后继同样方便 删*p本身和删*p的后继同样方便

§2.3.3 双链表(循环) 例1:前插 例2:删*p s->data=x; // ② s->prior=p->prior; // ③ s->next=p; // ④ p->prior->next=s; // ⑤ p->prior=s; // ⑥ p->prior->next=p->next; p->next->prior=p->prior; free(p);

§2.3.4 静态链表 上述链表是由指针实现的,其中结点分配和回收是由系统实现的,系统提供了malloc和free动态执行,故称为动态链表。 动态----程序执行时系统动态分配和回收 静态链表---- 先分配大空间作为备用结点空间(即存储池) 用下标(亦称游标cursor)模拟指针,自定义分配和释放结点函数

§2.3.4 静态链表 1.定义 #define nil 0 //空指针 #define MaxSize 1000 //可容纳的结点数 typedef struct { DataType data; int next; }node, NodePool[MaxSize]; typedef int StaticList;

§2.3.4 静态链表 2.基本操作(模拟动态链表) 初始化 将整个表空间链成一个备用链表,只做一次。 void Initiate(NodePool space) { //备用链表的头结点位置为space[0] for (i=0; i<MaxSize-1; i++) space[i].next = i+1; space[MaxSize-1].next = nil; }

§2.3.4 静态链表 分配结点 在备用链表上申请一个结点,返回非空(不等于0)成功,否则失败。 int AllocNode(NodePool space) { i = space[0].next; //取备用链表上开始结点,位置为i,即space的第i个分量 if (i) //开始结点非空 space[0].next = space[i].next; //将开始结点从备用链表上摘下 return i; //为nil时失效 }

§2.3.4 静态链表 释放结点 将释放结点收还到备用链表的头上。 void FreeNode(NodePool space, int ptr) { //将ptr所指结点回收到备用链表头结点之后 space[ptr].next = space[0].next; space[0].next = ptr; }

§2.3.4 静态链表 3.一般操作 插入 void Insert(NodePool space, StaticList L, DataType x, int i) { // 在带头结点的静态链表L的第i个结点ai之前插入新结点x p = Locate(space, L, i-1); //找L中ai的前驱① if (p == nil) Error(“Position error!”); //位置错 S = AllocNode(space); //申请结点② if (s == nil) Error(“Overflow”); //申请失败,空间不足判定 space[s].data = x; // ③ space[s].next = space[p].next; // ④ space[p].next = s; // ⑤ }

§2.3.4 静态链表 注意:链表头指针实际上整数,即space的下标 删除 void Delete(NodePool space, StaticList L, int i) { p = Locate(space, L, i-1); // 找L中ai的前驱 ① if (p == nil || space[p].next == nil) Error(“Position error!”); // 位置错 q = space[p].next; // q指向被删结点ai ② space[p].next = space[q].next; // 将ai摘下 ③ FreeNode(space, q); // ④ } 注意:链表头指针实际上整数,即space的下标

§2.3.4 静态链表

§2.3.4 静态链表

§2.3.4 静态链表 在spce中可能有多个链表,若再建立一个链表head,头结点在哪个位置?

§2.3.4 静态链表 静态链表特点: §2.4 顺序表和链表之比较 没有指针类型的语言可以使用。亦可有双链表。循环链表等。 对比: 动态链表 静态链表 指针变量ptr 下标ptr 结点变量*ptr 结点space[ptr] 结点后继位置ptr->next 结点后继位置space[ptr].next §2.4 顺序表和链表之比较

作业 为什么在单循环链表中设置尾指针比设置头指针更好? 写一个算法将单链表中值重复的结点删除,使所得的结果表中各结点值均不相同。