文章目录
1 线性表的类型定义
线性表(linear list)是最常用且最简单的一种数据类型。
1.1 定义
一个线性表是n个数据元素的有限序列。
至于每个数据元素的具体含义,在不同情况下各不相同。
1.2 记录和文件
在稍复杂的线性表中,一个数据元素可以由若干个数据项(item)构成。
在这种情况下,常把数据元素称为记录(record),含有大量纪录的线性表又称文件(file)
1.3 形式表示
线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同特性,即属于同一数据对象,相邻数据元素之间存在着序偶关系。
序偶关系:有序、成对
若将线性表记为
1.3.1 前驱后继
则表中领先于,领先于,称是的直接前驱元素, 是的直接后继元素
当 时,有且只有一个直接后继,
当 时,有且只有一个直接前驱。
1.3.2 线性结构的特点
线性表中第一个元素称为表头元素,最后一个元素称为表尾元素
只有一个首结点和尾结点
除首尾结点外,其他结点只有一个直接前驱和一个直接后继
1.4 线性表的长度
线性表中元素的个数定义为线性表的长度,时称为空表。
在非空表中的每一个数据元素都有一个确定的位置
如是第一个元素,是最后一个元素
是第个元素,称为数据元素在线性表中的位序
2 线性表的顺序表示和实现
2.1 定义
线性表的顺序表示指的是用一组地址连续的存储单元一次存储线性表的数据结构
2.1.1 静态分配空间
数组可以静态分配(大小固定)
2.1.2 动态分配空间
也可以动态分配(利用动态分配语句)
动态分配并不是链式存储,同样还是属于顺序存储结构,只是分配的空间大小可以在运行时决定
2.2 顺序表的三大部分
2.2.1 存储空间的起始位置
2.2.2 顺序表最大存储容量
2.2.3 顺序表当前的长度
2.3 数据元素存储位置的计算方法
假设线性表的每个元素需占用个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。
则线性表中第个数据元素的存储位置和第个数据元素的存储位置之间满足下列关系:
一般来说,线性表的第个数据元素的存储位置为
式中是线性表的第一个数据元素的存储位置,通常称做线性表的起始位置或基位置。
2.4 特点
-
只要确定了线性表的起始位置,线性表中任一数据元素都可随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构
-
利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致
-
在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等
这种存取元素的方法被称为随机存储法
2.5 时空复杂度分析
查找,插入,删除算法的平均时间复杂度为
顺序表的空间复杂度(没有占用辅助空间)
2.6 顺序表的优缺点
2.6.1 优点
-
存储密度大(结点本身所占存储量/结点结构所占存储量)
-
可以随机存取表中任一元素
2.6.2 缺点
-
在插入、删除某一元素时,需要移动大量元素
-
浪费存储空间,对存储空间要求高,会产生存储空间的“碎片”
-
属于静态存储形式,数据元素的个数不能自由扩充
为了克服这一缺点,我们可以采用链式存储结构
3 线性表的链式表示和实现
它不要求逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构所具有的弱点,但同时也失去了顺序表可随机存取得优点
3.1 线性链表
3.1.1 特点
线性表的链式存储结构的特点是
-
用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)
-
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
-
访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等
这种存取元素的方法被称为顺序存取法
3.1.2 结点
一部分存储数据元素信息,另一部分存储直接后继存储位置。
3.1.2.1 数据域
存储数据元素信息的域称为数据域
3.1.2.2 指针域
存储直接后继存储位置的域称为指针域
指针域中存储的信息称做指针或链。
3.1.3 链表
个结点链接称一个链表,即为线性表的链式存储结构
又由于此链表的每个结点中只包含一个指针域
故又称 线性链表 或 单链表。
3.1.4 头结点
3.1.4.1 定义
有时我们在单链表的第一个结点之前附设一个结点,称之为头结点。
头结点的数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息
头结点的指针域存储指向第一个节点的指针(即第一个元素结点的存储位置)
3.1.4.2 作用
-
处理操作起来方便(统一代码,减少特判,相当于哨兵)
-
无论链表是否为空,其头指针是指向头结点的非空指针,因此空表和非空表的处理也就统一了
3.1.5 头结点与头指针的区别
-
不管带不带头结点,头指针始终指向链表的第一个结点
-
而头结点是带头结点链表中的第一个结点,节点内通常不存储信息,它是为了方便做的一种处理
3.2 循环链表
3.2.1 定义
表中最后一个结点的指针域指向头结点,整个链表形成一个环
3.2.2 特点
从表中任一结点出发均可找到表中其他结点。
3.3 双向链表
为了克服单链表这种单向性的缺点,可以利用双向链表
在双向链表的结点中有两个指针域,其一指向直接后继,另一指向直接前驱
3.4 链表的优缺点
3.4.1 优点
-
数据元素的个数可以自由扩充
-
插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高
3.4.2 缺点
-
存储密度小
-
存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)
3.5 顺序表与链表的比较
4 一元多项式的表示及相加
4.1 结点
typedef struct PNode{
float coef;//系数
int expn; //指数
struct PNode *next; //指针域
}PNode, *Polynomial;
4.2 多项式的创建
4.2.1 创建一个只有头结点的空链表
4.2.2 根据多项式的项的个数n,循环n次执行以下操作:
-
生成一个新结点*s;
-
输入多项式当前项的系数和指数赋给新结点*s的数据域;
-
设置一前驱指针pre,用于指向待找到的第一个大于输入项指数的结点的前驱,pre初值指向头结点;
-
指针q初始化,指向首元结点;
-
循链向下逐个比较链表中当前结点与输入项指数,找到第一个大于输入项指数的结点*q;
-
将输入项结点s插入到结点q之前。
4.2.3 算法描述
void CreatePolyn(Polynomial &P,int n) {
//输入m项的系数和指数,建立表示多项式的有序链表P
P = new PNode;
P->next = NULL; //先建立一个带头结点的单链表
for(i = 1; i <= n; ++i) { //依次输入n个非零项
s=new PNode; //生成新结点
scanf("%d %d", &(s->coef), &(s->expn)); //输入系数和指数
pre = P; //pre用于保存q的前驱,初值为头结点
q = P->next; //q初始化,指向首元结点
while(q && q->expn < s->expn) { //找到第一个大于输入项指数的项*q
pre = q;
q = q->next;
}
s->next = q; //将输入项s插入到q和其前驱结点pre之间
pre->next = s;
}
}
4.3 多项式相加
4.3.1 指针p1和p2初始化,分别指向Pa和Pb的首元结点
4.3.2 p3指向和多项式的当前结点,初值为Pa的头结点
4.3.3 当指针p1和p2均未到达相应表尾时,则循环比较p1和p2所指结点对应的指数值(p1->expn与p2->expn)
有下列3种情况:
-
当p1->expn等于p2->expn时,则将两个结点中的系数相加,若和不为零,则修改p1所指结点的系数值,同时删除p2所指结点,若和为零,则删除p1和p2所指结点;
-
当p1->expn小于p2->expn时,则应摘取p1所指结点插入到“和多项式”链表中去;
-
当p1->expn大于p2->expn时,则应摘取p2所指结点插入到“和多项式”链表中去。