线性结构的特点:在数据元素非空
1. 存在唯一地一个被称作“第一个”的数据元素
2. 存在唯一地一个呗称作“最后一个”的数据元素
3. 除第一个之外,集合中的每个数据元素均只有一个前驱
4. 除最后一个之外,集合中每个数据均只有一个后继
线性表
由零个或多个数据元素组成的有限序列。
ADT
ADT list{
数据对象:D={ai| ai∈ElemSet, i=1,..,n,n≥0}
数据关系:R={<ai-1,ai>| ai-1,ai∈D, i=2,...n}
基本操作:
InitList(&L)
操作结果:构造一个空的线性表L
DestroyList(&L)
初始条件:L已存在
操作结果:销毁线性表L
ClearList(&L)
初始条件:线性表L已存在
操作结果:将L重置为空表
}
顺序存储结构
#define MAXSIZE 20
typedef int ElemType
typedef struct{
ElemType data[MAXSIZE];
int length;
}SqList;
一般是从下标1开始
适合元素个数比较稳定,不经常插入和删除元素,而更多的操作是存取数据的应用。
优点:
- 无需为表示表中元素之间的逻辑关系而增加额外的存储空间
- 可以快速地存取表中任意位置的元素
缺点:
- 插入和删除操作需要移动大量元素
- 当线性表长度变化较大时,难以确定存储空间的容量
- 容易造成存储空间的“碎片”
一大块一大块的申请,中间的小碎片就浪费了
链式存储结构
数据域:存储数据元素信息的域
指针域:存储直接后继位置的域。存储的信息称为指针或域。
这两部分信息组成的数据元素称为存储映像,称为结点(Node)
单链表
头指针:指向头结点的指针
头结点:
1. 第一个结点
3. 一般不存储任何信息,一般用于存储长度
4. 无论链表是否为空,头结点均不为空
typedef struct Node{
ElemType data; //数据域
struct Node *next; //指针域
}Node;
typedef struct Node* LinkList; //头指针
静态链表
静态链表:用数组描述的链表,也叫游标实现法
#define MAXSIZE 1000
typedef struct{
ElemType data; //数据
int cur; //游标(Cursor)
}Component,StaticLinkList[MAXSIZE];
指针下的数组顺序:[999]–>[1]–>[2]–>[3]–>[4]–>[0]–>[5]–>[6]–>…–>[998]–>[999]
分为两部分:
- 具有数据的数据链:[999]–>[1]–>[2]–>[3]–>[4]
- 还未存储数据的空链:[0]–>[5]–>[6]–>…–>[998]
中间,用数据链的最后一个元素[4]指向空链的头指针[0]
最后,空链的最后一个元素[998]–>[999]
如下图,解释四个特殊数据:
1. list[0].游标–>空链的头指针
2. list[MAX_SIZE-1].游标–>数据链的头指针
3. 游标值为0–>list[4]是数据链的结尾,指向空链的头指针[0]
4. 空链的最后元素list[998]指向数据链的头指针[999]
理解:用一个游标数组,将离散的数据串起来。由于有一些位置还没有数据,所以用[0].cur作为中间连接点(有数据和没数据的界限),连接数据链与空链
优点:在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了再顺序存储结构中的插入和删除操作需要移动大量元素的缺点
缺点:
1. 没有解决连续存储分配带来的表长难以确定的问题
2. 失去了顺序存储结构随机存取的特性
循环链表
循环链表:将单链表终端节点的指针端由空指针改为指向头结点,使整个单链表行成一个环
特点:无须增加存储量,仅对链接方式稍作改变,即可使得表处理更加方便灵活。
双向链表
相比于单链表,每个结点多了一个prior指针
双向链表有效提高算法的时间性能,用空间换取时间
对比
方面 | 顺序存储 | 链式存储 |
---|---|---|
分配方式 | 一段连续的空间 容易产生内存空洞 |
用一组任意的存储单元 能够把零散的空间使用起来 |
查找 | O(1) | O(n) |
插入 | O(n) | O(1) |
空间性能 | 需要预先分配空间,不好控制 | 灵活分配,元素个数不受限制 |