33_顺序表(待完善)

核心摘要: 本文使用C语言从零实现动态顺序表,重点讲解内存分配策略、增删改查的核心算法以及边界条件处理,解决"数组越界"和"扩容无效"两大常见问题。


一、顺序表的基本概念与结构设计

1. 逻辑结构与物理结构对比

   (1) 逻辑结构:线性表(元素之间具有一对一的前后关系)。

   (2) 物理结构:连续的内存地址(与数组一致,但支持动态扩展)。

2. 静态数组 vs 动态顺序表

   (1) 静态数组的致命缺陷:容量固定,编译时确定,易溢出。

   (2) 动态顺序表的优势:运行时扩容,内存利用率高。

3. 结构体定义(C语言描述)

   采用 SeqList 结构体封装三个必要属性:

c 复制代码
typedef int SLDataType;  // 方便后续修改数据类型(如改为char或double)

typedef struct SeqList
{
    SLDataType* a;      // 指向动态开辟数组的指针
    int size;           // 有效数据个数(当前长度)
    int capacity;       // 当前最大容量(总空间大小)
} SeqList;

    a. 为什么需要 capacity ------ 区分"已用空间"和"总空间",是扩容判断的依据。

    b. 为什么用 typedef 定义数据类型? ------ 提高代码复用性,一行修改即可适配int/float/struct。


二、核心操作的实现(增删改查)

1. 初始化与销毁(内存管理)

   (1) 初始化 :将指针置 NULLsizecapacity 置 0。

c 复制代码
void SLInit(SeqList* psl);

   (2) 销毁 :释放 psl->a,重新调用初始化(防止野指针)。

c 复制代码
void SLDestroy(SeqList* psl);

2. 扩容机制(核心难点)

   (1) 触发条件size == capacity

   (2) 扩容策略

    a. 小容量场景:capacity 为 0 时,开辟 4 个元素大小。

    b. 大容量场景:新容量 = 原容量 × 2(指数增长,减少 realloc 次数)。

   (3) 扩容函数实现

c 复制代码
void SLCheckCapacity(SeqList* psl)
{
   if (psl->size == psl->capacity)
   {
       int newCapacity = (psl->capacity == 0 ? 4 : psl->capacity * 2);
       SLDataType* tmp = (SLDataType*)realloc(psl->a, newCapacity * sizeof(SLDataType));
       if (tmp == NULL)
       {
           perror("realloc fail");
           return;
       }
       psl->a = tmp;
       psl->capacity = newCapacity;
   }

3. 增(头插 / 尾插 / 任意位置插入)

   (1) 尾插 :先检查容量,再在 psl->a[psl->size] 处赋值,最后 size++。时间复杂度 O(1)。

   (2) 头插 :从后往前移动元素(for 循环从 size1),腾出 [0] 位置。时间复杂度 O(n)。

   (3) 任意位置插入 :将 [pos, size-1] 全部后移一位,再插入。必须判断 pos 合法性(0 ≤ pos ≤ size)。

4. 删(头删 / 尾删 / 任意位置删除)

   (1) 尾删 :直接 size--(注意:不需要真正清除内存,下次插入会覆盖)。

   (2) 头删 :从前往后移动元素(for 循环从 1size-1),最后 size--

   (3) 任意位置删除 :将 [pos+1, size-1] 前移覆盖 pos 位置。

    ⚠️ 删除操作的三重检查:

    ⓵ 断言 psl 不为空。

    ⓶ 断言 psl->size > 0(空表不能删)。

    ⓷ 检查 pos 范围:0 ≤ pos < size。

5. 查与改

   (1) 按值查找:遍历数组,返回第一个匹配的下标(找不到返回 -1)。

   (2) 按位置修改 :直接赋值 psl->a[pos] = x前提是 pos < size

   (3) 按值修改:先查找位置,再修改(查找逻辑复用)。


三、打印与调试辅助函数

1. 打印顺序表

   遍历 size 次,输出 psl->a[i],格式统一为 [1, 2, 3, 4]

2. 菜单交互示例(用于控制台测试)

   提供 0-7 的数字菜单:

   ⓵ 尾插   ⓶ 头插   ⓷ 尾删   ⓸ 头删

   ⓹ 任意插入   ⓺ 任意删除   ⓻ 查找   ⓼ 打印   ⓿ 退出


四、常见错误与避坑指南(实用性重点)

1. 扩容时的野指针问题

   (1) 错误写法 :直接用 realloc(psl->a, newSize) 且不判断返回值。

   (2) 正确写法 :用临时指针 tmp 接收 realloc 返回值,成功后再赋值给 psl->a

    原因realloc 失败返回 NULL,直接赋值会丢失原有内存地址,导致内存泄漏且无法恢复。

2. 删除/插入时的越界访问

   (1) 头插移动元素时,必须从后往前移动for (i = size; i > pos; i--))。

   (2) 头删移动元素时,必须从前往后移动for (i = pos; i < size-1; i++))。

    口诀:插入向后挪,删除向前盖;反向操作必越界。

3. 结构体传值 vs 传址

   (1) 错误 :函数参数写 void func(SeqList sl),修改的是临时副本。

   (2) 正确 :一律传指针 void func(SeqList* psl),用 -> 访问成员。

4. 缩容的误区

   不建议在删除元素时立即缩容(频繁缩容会导致性能抖动)。业界通用做法:只扩不缩 ,或仅在剩余空间占比极低时(如 size < capacity/4)考虑缩容。


五、完整代码文件结构(项目组织)

1. 分文件编写(工业级规范)

   (1) SeqList.h ------ 头文件:结构体定义、函数声明、宏定义。

   (2) SeqList.c ------ 源文件:所有函数的具体实现。

   (3) test.c ------ 测试文件:main 函数和菜单交互。

2. 头文件中的防重复包含

c 复制代码
#ifndef _SEQLIST_H_
#define _SEQLIST_H_

// 所有声明放在这里

#endif

3. 编译命令示例(gcc)

bash 复制代码
gcc -o test SeqList.c test.c -std=c99 -Wall

六、顺序表的性能总结与适用场景

1. 时间复杂度汇总

操作 时间复杂度 备注
尾插 / 尾删 O(1) 平均情况(考虑扩容均摊后仍为 O(1))
头插 / 头删 / 任意位置插入删除 O(n) 需要移动大量元素
按位置访问 O(1) 随机访问是顺序表的最大优势
按值查找 O(n) 无序情况下必须遍历

2. 适用场景(什么时候用顺序表)

   ⓵ 需要频繁随机访问(如通过下标取第 i 个元素)。

   ⓶ 插入/删除操作只在尾部进行(栈结构)。

   ⓷ 元素个数可预估,或对内存连续有硬性要求(如 DMA 传输)。

3. 不适用场景(什么时候该换链表)

   ⓵ 频繁在头部或中间插入/删除。

   ⓶ 元素个数极其不稳定,且单个元素体积很大(扩容拷贝成本高)。

相关推荐
楷哥爱开发10 分钟前
降低网络爬虫成本:基础设施优化指南
服务器·开发语言·php
__Rhaast丶14 分钟前
set_data_check用法解析(一) lib库中的data check解析
单片机·嵌入式硬件
鱼很腾apoc18 分钟前
【Linux】第7期 进程间通信 (IPC) 详解:管道 (匿名 / 命名) + System V
linux·服务器·c语言·学习·进程间通信·ipc
毒爪的小新20 分钟前
踩坑实录 | RAG知识库完整搭建-Milvus2.4+BGE大中文AI模型嵌入
linux·人工智能·ai·milvus·rag
YuK.W27 分钟前
Leetcode100: 94.二叉树中序遍历、104.二叉树最大深度、226.翻转二叉树
java·算法·leetcode·二叉树
yxl8746464630 分钟前
PCTG-1015型Profinet转Ethernet/IP协议转换器
服务器·网络·物联网·网络协议·自动化·信息与通信
你觉得脆皮鸡好吃吗32 分钟前
【THM】JWT Security & Protocols and Servers(AI)
运维·服务器·网络
2023自学中1 小时前
imx6ull 开发板, mame 模拟器,运行游戏 测试
linux·游戏·嵌入式·开发板
云飞云共享云桌面1 小时前
搭建10人SolidWorks云设计环境:云飞云在非标自动化工厂的实测方案
运维·服务器·网络·数据库·自动化·电脑
是个西兰花1 小时前
Linux:进程信号
linux·运维·服务器