线性表
定义
由**零个**或多个数据元素组成的有限序列
(a1,a2,a3,..., an), 其中 a1 为 a2 的前驱元素, a3 为 a2 的后继元素
n 为线性表的长度, n = 0,时为空表
数据类型
定义: 是指一组性质相同的值得集合及定义在此集合上的一些操作的总称
例如: 整形、浮点型、字符型、双浮点型等等
分类: - 原子类型: 不可再分解的基本类型, 例如:整形、浮点型等
- 结构类型: 由若干基本类型组合而成, 可再分解, 例如: 整形数组等等
有利于计算机合理分配空间
抽象数据类型
是指一个数学模型及定义在该模型上的一组操作,即一个把属性和方法捆绑在一起,形成一个类
“抽象” 的意义是对数据类型进行数据抽象
伪代码:
ADT 抽象数据类型名
Data
数据元素之间的逻辑关系的定义
Operation
操作
endADT
线性表的抽象数据类型
ADT 线性表(List)
Data
线性表的数据对象集合为{a1, a2, ..., an},
每个元素的类型均为 dataType。
其中,除了第一个元素 a1 外,每个元素都有且仅有一个前驱元素,
除了最后一个元素 a2 外, 每个元素都有且仅有一个后继元素。
Operation
InitList(*L): 初始化操作, 建立一个空的线性表 L。
ListEmpty(L): 判断线性表是否为空表, 是否为空表,若是返回 true,否则返回 false
ClearList(*L): 清空线性表
GetElement(L, i, *e): 将线性表 L 中的第 i 个元素值返回给e
LocateElement(L, e): 在线性表 L 中查找与给定值 e 相等的元素, 如果查找成功, 则返回该元素的在表中的序号, 否则返回0
ListInsert(*L, i, e): 在线性表 L 中序号为 i 的位置插入新元素 e
ListDelete(*L, i, *e): 删除线性表 L 中的第 i 个位置的元素, 并用 e 返回其值
ListLength(*L): 返回线性表 L 的长度
endADT
线性表的顺序存储结构
- 结构代码
#define MAXSIZE 20
typedef int ElemType
typedef struct
{
ElemType data[MAXSIZE];
int length; // 线性表当前的长度
} SqList;
- 线性表结构封装的三个属性:
- 地址计算方法
设 ElemType 占用 c 个存储单元(字节),
那么第 i+n 个位置的地址为:
LOC(a[i+n]) = LOC(ai) + nc;
第 i 个位置的地址为:
LOC(ai) = LOC(a1) + (i-1)*c
- 线性表删除和插入操作的时间复杂度
最好情况为: O(1)
最坏情况为: O(n)
平均情况为: O( (n+1)/2 ) 约等于O(n)
- 优点:
- 无须为表示表中的元素之间的逻辑关系而增加额外的存储空间
- 可以快速地存取表中任意位置的元素
- 缺点:
- 插入和删除操作需要移动到大量的元素
- 当线性表的长度变化较大是, 难以确定存储空间的容量
- 容易造成存储空间的“碎片”
线性表的链式存储结构
1. 概念:
- 数据域: 存储元素信息的域
- 指针域: 存储后继位置的域,所存储的信息为指针或链
- 存储映像: 数据域 + 指针域, 又称为结点(Node)
- 链表: n 个结点链接成的线性表
2. 单链表
- 头指针:
- 指向第一个结点的指针
- 具有标识作用, 长用链表名字命名
- 无论链表是否为空,头指针都不为空
- 头指针是链表的必要元素
- 结构代码
typedef struct Node
{
ElemType data; // 数据域
struct Node *Next; // 指针域
} Node;
typedef struct Node* LinkList;
- 单链表读取:
核心思想: **工作指针后移 **
- 时间复杂度:
- 第一次查询、插入、删除操作的时间复杂度都是 O(n)
- 接下来每次操作都只通过需要移动指针就可以了, 时间复杂度为 O(1)
- 因此对于操作越频繁的数据来说, 优势就越明显
- 整表创建:
- 头插法创建单链表
- 创建一个头指针,生成一个悬浮的数据结点 p
- 将 p 的地址存到头指针中
- 再生成一个 悬浮数据结点 p, 在 p->Next 中存储头指针的地址
- 将 p 的地址存放到头指针中
- 以此类推
- 尾插法创建单链表
- 创建一个头指针, 生成一个尾指针 r (最初尾与头指针相同),生成一个悬浮数据结点 p
- 将 p 的地址存到 r->Next 中
- 再让 r 指向 p;
- 再生成一个悬浮数据结点 p
- 将新的 p 结点的地址存到 r->Next 中
- 再让 r 指向 p;
- 以此类推
- 单链表与顺序存储结构的优缺点
- 分配方式
- 顺序存储结构用一段连续的存储单元以此存储线性表的数据元素
- 单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素及连接地址
- 时间性能
- 查找
- 顺序存储结构 O(1)
- 单链表 O(n)
- 插入和删除
- 顺序表 O(n)
- 单链表 第一次 O(n), 后面为 O(1)
- 空间性能
- 顺序表的预分配一定的存储空间,分配小了会溢出,分配大了会浪费空间,灵活性低
- 单链表不用预分配空间,在需要是即时分配,可以存储的元素不受限制,灵活性高
- 结论:
- 若线性表需要经常性的查找,无经常插入和删除的话采用顺序存储结构,否则,采用链式存储结构
- 若不清楚所需存储空间时,建议使用单链表
3. 静态链表
- 概念: 用数组描述的链表叫静态链表 (游标实现法)
- 结构代码
#define MAXSIZE 1000
typedef struct
{
ElemType data; // 数据
int cur; // 游标(cursor)
} Component, StaticLinkList[MAXSIZE];
- 初始化
initStaticLinkList(StaticLinkList space)
{
int i;
for ( i = 0; i < MAXSIZE -1; i++) {
space[i].cur = i + 1;
}
space[MAXSIZE - 1].cur = 0;
return OK;
}
- 静态链表的插入
- 将插入的元素放到备用链表的头部 headCur
- 将 headCur 的游标指向 i+1 位置的下标(即为原来 i-1 的游标)
- 在 i-1 位置的游标存入 i 位置的下标
- 如果为空表,则默认插在下标为 1 的位置
4. 循环链表
- 将单链表的尾结点指针指向头结点,就形成了首尾闭合的循环链表
- 空表:head->next 指向 head 或 为 null
- 尾部结点: rear
5. 双向链表
- 结构代码
typedef struct DualNode
{
ElemType data; // 数据域
struct DualNode *Next; // 后驱结点指针
struct DualNode *prior; // 前驱结点指针
} DualNode;
typedef struct DualNode *DuLinkList ;