这里主要说四大模块:线性表(顺序表与链表的存储实现、操作及对比)、栈(LIFO特性、顺序/链式实现及表达式求值应用)、队列(FIFO特性、循环队列解决假溢出问题)和串(BF/KMP模式匹配算法)。文档通过C语言代码示例、时间复杂度分析、对比表格和真题解析,重点突出线性结构的存储原理、基本操作实现(如链表插入删除、栈的括号匹配)以及典型应用场景(如递归调用、BFS算法),比较重要的知识点:
-
线性表
-
顺序表(数组存储,快速访问但插入删除慢)
-
链表(指针链接,动态内存分配,插入删除快)
-
包含单链表、循环链表和双向链表等变体
-
栈
-
后进先出(LIFO)结构
-
顺序栈和链栈两种实现
-
典型应用:函数调用、表达式求值、括号匹配
-
队列
-
先进先出(FIFO)结构
-
重点掌握循环队列解决"假溢出"问题
-
链队列的实现方式
-
串
-
字符串的存储结构
-
基础的模式匹配算法(BF算法)
-
(部分教材包含)KMP算法思想
知识拓扑

知识点介绍
1. 线性表
1.1 基本概念与逻辑结构
定义:线性表是由n(n≥0)个相同类型数据元素构成的有限序列,记作(a₁,a₂,...,aₙ)。其中:
-
元素个数n称为表长
-
n=0时称为空表
-
存在唯一"第一个"和"最后一个"元素
重要特性:
-
元素类型相同
-
元素具有序偶关系
-
每个元素最多有一个直接前驱和一个直接后继
// 抽象数据类型(ADT)标准定义
ADT List {
数据对象:D = {aᵢ | aᵢ ∈ ElemSet, i=1,2,...,n, n≥0}
数据关系:R = {<aᵢ₋₁,aᵢ> | aᵢ₋₁,aᵢ ∈ D, i=2,...,n}
基本操作:
InitList(&L); // 构造空线性表
DestroyList(&L); // 销毁线性表
ListEmpty(L); // 判空
ListLength(L); // 求表长
GetElem(L,i,&e); // 按位查找
LocateElem(L,e); // 按值查找
ListInsert(&L,i,e); // 插入元素
ListDelete(&L,i,&e); // 删除元素
} ADT List
1.2 顺序存储结构详解
物理结构:用一组地址连续的存储单元依次存放线性表的元素
存储特点:
-
逻辑上相邻的元素物理位置也相邻
-
随机存取特性(通过首地址+偏移量直接访问)
-
需要预分配固定大小的存储空间
元素位置公式:
LOC(aᵢ) = LOC(a₁) + (i-1)*sizeof(ElemType)
插入操作算法分析:
#define MAXSIZE 100 // 顺序表最大容量
typedef struct {
ElemType data[MAXSIZE]; // 存储空间基址
int length; // 当前长度
} SqList;
Status ListInsert(SqList &L, int i, ElemType e) {
// 在顺序表L的第i个位置插入新元素e
if (i<1 || i>L.length+1) return ERROR; // 位置不合法
if (L.length >= MAXSIZE) return ERROR; // 存储空间已满
for (int j=L.length; j>=i; j--)
L.data[j] = L.data[j-1]; // 插入位置及之后的元素后移
L.data[i-1] = e; // 放入新元素
L.length++; // 表长增1
return OK;
}
时间复杂度分析:
最好情况(表尾插入):O(1)
最坏情况(表头插入):O(n)
平均情况:Σ(i=1→n+1) pᵢ(n-i+1) = n/2 → O(n)
1.3 链式存储结构详解
结点结构:
typedef struct LNode { // 结点类型定义
ElemType data; // 数据域
struct LNode *next; // 指针域
} LNode, *LinkList;
头结点作用:
-
统一空表和非空表的处理
-
方便首元结点的插入/删除操作
-
使链表操作参数统一
单链表创建(尾插法):
void CreateList_R(LinkList &L, int n) {
L = (LinkList)malloc(sizeof(LNode)); // 创建头结点
L->next = NULL;
LNode *r = L; // 尾指针初始化
for (int i=0; i<n; ++i) {
LNode *p = (LNode*)malloc(sizeof(LNode));
scanf("%d", &p->data); // 输入元素值
p->next = NULL;
r->next = p; // 插入到表尾
r = p; // 更新尾指针
}
}
双向链表优势:
-
可双向遍历
-
删除操作更高效(无需定位前驱)
typedef struct DuLNode {
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
} DuLNode, *DuLinkList;
1.4 应用场景对比分析
| 对比维度 | 顺序表 | 链表 |
|---|---|---|
| 存储密度 | 100% | <100%(需存储指针) |
| 访问方式 | 随机访问(O(1)) | 顺序访问(O(n)) |
| 插入/删除 | 平均移动n/2个元素(O(n)) | 修改指针(O(1)) |
| 空间分配 | 静态分配(可能浪费/溢出) | 动态分配(按需申请) |
| 典型应用 | 数据量固定、查询频繁的场景 | 数据量变化大、频繁增删的场景 |
2. 栈
2.1 栈的定义与特性
后进先出(LIFO)原理:
-
限定仅在表尾(栈顶)进行插入和删除操作
-
栈底固定,栈顶动态变化
数学性质:卡特兰数(Catalan number)计算合法的出栈顺序数:
Cₙ = (1/(n+1)) * C(2n,n)
2.2 两种实现方式对比
顺序栈定义:
#define STACK_INIT_SIZE 100
typedef struct {
SElemType *base; // 栈底指针
SElemType *top; // 栈顶指针
int stacksize; // 当前分配空间
} SqStack;
链栈优势:
-
无需考虑栈满情况
-
多栈共享存储空间更方便
2.3 栈的经典应用场景
递归调用实现:
// 阶乘函数的递归实现
int Factorial(int n) {
if (n==0) return 1;
else return n * Factorial(n-1);
}
系统使用调用栈存储:
返回地址
参数传递
局部变量
表达式求值算法:
中缀表达式:3*(4+5)/2 转换步骤: 1. 初始化操作数栈和运算符栈 2. 遇到数字直接入操作数栈 3. 遇到运算符与栈顶比较优先级... 最终结果:3 4 5 + * 2 /
(后续章节内容因篇幅限制暂略,完整版包含所有章节的详细实现和真题解析)
5. 知识图谱

6. 真题破解
2023年真题:设计算法判断链表是否中心对称
bool isSymmetrical(LinkList L) {
// 1. 使用快慢指针找到中点
LNode *slow = L->next, *fast = L->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
// 2. 反转后半部分链表
LNode *pre = NULL, *cur = slow;
while (cur) {
LNode *temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
// 3. 前后半部分比较
LNode *p1 = L->next, *p2 = pre;
while (p2) {
if (p1->data != p2->data) return false;
p1 = p1->next;
p2 = p2->next;
}
return true;
}
自考数据结构(20331)- 线性结构&spm=1001.2101.3001.5002&articleId=146543034&d=1&t=3&u=7c97794d3626468c8117374b4dbee0b0)
2151

被折叠的 条评论
为什么被折叠?



