1. 顺序表基础与核心概念
顺序表作为线性表最基础的存储结构之一,本质上是用一组地址连续的存储单元依次存储数据元素。这种物理结构上的连续性使得顺序表具有随机访问的特性——我们可以通过首地址和元素序号在O(1)时间内定位到任意元素。
在C语言中,顺序表通常通过数组来实现。但与普通数组不同,顺序表需要动态管理存储空间和当前元素数量。这里的关键数据结构包含两个部分:
- 数据存储区:用malloc动态分配的连续内存块
- 表结构体:记录当前容量(size)和元素个数(length)
c复制#define INIT_SIZE 10
typedef struct {
int *data; // 存储空间基址
int length; // 当前元素个数
int size; // 当前存储容量
} SeqList;
这种设计既保持了数组的高效访问特性,又实现了动态扩容的能力。当插入元素导致空间不足时,可以重新分配更大的内存块(通常是原大小的1.5或2倍),然后将原有数据复制到新空间。
注意:顺序表的索引从0开始计算,这与数组保持一致。但在用户接口层,可以根据需求设计为从1开始计数,内部再做转换。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 顺序表初始化与内存管理
2.1 初始化实现细节
顺序表的初始化需要完成三项关键操作:
- 分配初始存储空间
- 设置初始容量
- 将长度计数器归零
c复制SeqList* InitList() {
SeqList *L = (SeqList*)malloc(sizeof(SeqList));
L->data = (int*)malloc(INIT_SIZE * sizeof(int));
if(!L->data) exit(ERROR); // 分配失败处理
L->length = 0;
L->size = INIT_SIZE;
return L;
}
这里有几个值得注意的技术点:
- 进行了二级内存分配:先分配表结构体,再分配数据存储区
- 添加了错误处理,当malloc失败时直接退出程序
- 使用宏定义INIT_SIZE作为初始容量,便于统一修改
2.2 动态扩容策略
当插入元素导致length >= size时,需要进行扩容操作。扩容的基本步骤是:
- 分配新的更大内存空间
- 复制原有数据到新空间
- 释放旧空间
- 更新容量参数
c复制void ExpandList(SeqList *L) {
int new_size = L->size * 2; // 常见的扩容策略
int *new_data = (int*)realloc(L->data, new_size * sizeof(int));
if(!new_data) exit(ERROR);
L->data = new_data;
L->size = new_size;
}
实际开发中,realloc比malloc+memcpy更高效,因为它可能直接在原内存块后扩展空间,避免数据拷贝。但要注意realloc失败时需要保持原数据不变。
经验:在嵌入式等内存紧张环境中,可以采用更保守的扩容策略(如每次增加固定大小),避免一次性分配过大内存导致失败。
3. 核心操作实现详解
3.1 元素插入操作
顺序表的插入需要处理三种位置情况:
- 头部插入
- 中间插入
- 尾部插入
无论哪种情况,都需要先检查是否需要扩容,然后将插入位置后的元素依次后移:
c复制int ListInsert(SeqList *L, int index, int elem) {
if(index < 0 || index > L->length)
return ERROR; // 越界检查
if(L->length >= L->size)
ExpandList(L); // 空间不足时扩容
// 从后向前移动元素
for(int i = L->length; i > index; i--)
L->data[i] = L->data[i-1]
