线性表、链表、栈和队列是编程中常用的数据结构。
数据的逻辑结构
逻辑结构:是数据的组织形式,用来表示数据之间的逻辑关系,其结构由数据元素的集合和元素之间的关系组成。
三种基本逻辑结构
- 线性结构:数据元素之间为一对一前后连接的关系
- 树形结构:只有一个处在最高层次的数据元素无前结点,为根;其余元素均有且只有一个前结点,后结点无个数限制
- 图结构:每一元素均可有任意的前后结点,任意两结点可连接
数据的物理结构
数据元素及其关系在存储器中的存放形式称为物理结构,即存储结构。
物理结构分类
- 顺序存储:元素按某种顺序存储在连续的存储单元中,存储位置间关系反映元素间逻辑关系
- 链式存储:元素存储在不一定连续的存储单元,通过在元素中附加信息来表示与其想关的一个或多个其他元素的物理地址来建立元素间的逻辑关系
- 索引存储:将数据元素排成一个序列,每个元素对应一个索引,存储时建立附加的索引表,表中为元素的存储地址
- 散列存储:数据元素均匀存放在存储区中,在数据元素和其在存储器中的位置之间建立一个映射关系,根据该关系可得其存储地址
线性表
采用顺序存储结构的称为顺序表,采用链式存储结构的称为线性链表
顺序存储特点
- 优点:无需为元素间的逻辑关系增加额外存储空间;可随机存取
- 缺点:元素插入删除需进行大量元素移动,效率低;占用连续存储空间,且初始化时需确定大小
顺序表的定义
1 | const int maxsize=200; //最大长度 |
顺序表插入元素
1 | void Insert(SeqList *L,int i,ElemType x) { |
顺序表删除元素
1 | void Delete(SqeList *L,int i) { |
顺序表查找元素
1 | int Find(SeqList *L,ElemType x) { |
链式存储特点
- 优点:无需预先设置存储空间,灵活分配;插入删除无需移动额外元素
- 缺点:需要额外空间存储元素关系,数据域、指针域;查询效率低
链表定义
1 | struct LNode { |
单链表长度
1 | int Length() { |
在链表i位置插入新结点
1 | void Insert(LNode *head,int i,ElemType x) { |
从单链表中删除第i个结点
1 | void Delete(LNode *head,int i) { |
查找链表中的结点
1 | LNode* Find(LNode *head,ElemType x) { |
其他形式链表
- 循环链表:将单链表尾结点的指针由NULL改为指向头结点,首尾连接形成一个环形,为循环链表
- 双向链表:每个结点的指针域再增加一个指针,指向该结点的前一结点,形成两个不同方向的链
- 双向循环链表:将双链表的头结点的前趋指针指向尾结点,将尾结点的后继指针指向头结点
栈
- 只能在一端进行插入和删除操作的特殊线性表,允许进行插入删除操作的一端为栈顶,另一端为栈底
- 特点为先进后出(FILO,first in last out)或后进先出(LIFO,last in first out)
- 可应用于进制转换、括号/引号匹配检查、递归算法等
顺序栈定义
1 | struct SqStack { |
元素入栈
1 | void Push(SqStack *s,ElemType x) { |
元素出栈
1 | void Pop(SqStack *s,ElemType &e) { |
取栈顶元素
1 | void Peek(SqStack *s,ElemType &e) { |
队列
- 只能在表的一端进行插入操作,另一端进行删除操作的特殊线性表
- 允许删除元素的一端为队头(指针指空),允许插入元素的一端为队尾(指针指队尾元素),先进先出
- 主要应用于缓存,打印队列等
循环队列
- 将队列的头尾相连
- 当队尾和队头重叠时,约定为队空;当队尾加一后等于队头时,队满
链式队列
- 为队列的每一元素附加一个存储元素关系的指针域
队列定义
1 | const int MAX=100 |
入队操作
1 | void EnQueue(SqQueue &q,ElemType x) { |
出队操作
1 | void DeQueue(SqQueue &q) { |
取队头元素
1 | ElemType GetHead(SqQueue &q) { |