线性表
1.概述
线性表是具有相同数据类型的 n 个数据元素的有限序列。
2.基本特征
-
元素个数有限
-
逻辑上顺序排列
-
每个元素有且仅有一个直接前驱和直接后继
-
所有元素数据类型相同
3.线性表的两种实现方式
顺序表(Sequence List)
使用数组实现,元素在内存中连续存储。
#define MAXSIZE 100
typedef struct {
ElemType data[MAXSIZE]; // 静态分配
int length; // 当前长度
} SqList;
优缺点
-
优点:随机访问快 O(1)
-
缺点:插入删除慢 O(n),容量固定
链表(Linked List)
使用指针实现,元素在内存中非连续存储。
typedef struct LNode {
ElemType data; // 数据域
struct LNode *next; // 指针域
} LNode, *LinkList;
优缺点
-
优点:动态扩容,插入删除快 O(1)
-
缺点:不能随机访问,需要遍历 O(n)
4.线性表的基本操作
|------|-------------------------|-------------------------|
| 操作 | 顺序表 | 单链表 |
| 初始化 | InitList(&L) | InitList(&L) |
| 插入 | ListInsert(&L, i, e) | ListInsert(&L, i, e) |
| 删除 | ListDelete(&L, i, &e) | ListDelete(&L, i, &e) |
| 按值查找 | LocateElem(L, e) | LocateElem(L, e) |
| 按位查找 | GetElem(L, i) | GetElem(L, i) |
| 求长度 | Length(L) | GetElem(L, i) |
| 判空 | Empty(L) | Empty(L) |
| 销毁 | - | estroyList(&L) |
5.代码实例对比
顺序表插入操作
// 在顺序表L的第i个位置插入元素e
Status ListInsert_Sq(SqList &L, int i, ElemType 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++; // 长度增加
return OK;
}
单链表插入操作
// 在单链表L的第i个位置插入元素e
Status ListInsert_Link(LinkList &L, int i, ElemType e) {
LNode *p = L;
int j = 0;
// 找到第i-1个结点
while (p && j < i - 1) {
p = p->next;
j++;
}
if (!p || j > i - 1) return ERROR; // 位置校验
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
6.时间复杂度对比
|-------|------|------|
| 操作 | 顺序表 | 链表 |
| 按索引访问 | O(1) | O(n) |
| 插入/删除 | O(n) | O(1) |
| 头部操作 | O(n) | O(1) |
| 尾部操作 | O(1) | O(n) |
7. 应用场景
选择顺序表的情况:
-
需要频繁随机访问元素
-
已知或可预估数据量大小
-
插入删除操作较少
选择链表的情况:
-
数据量变化较大,需要动态扩容
-
频繁在任意位置插入删除
-
无法预估数据量大小