0基础玩转顺序表:核心概念 + 增删查改功能

发布时间:2026/10/24 23:53:29

0基础玩转顺序表:核心概念 + 增删查改功能 玩转顺序表理解核心概念 实现增删查改功能阅读本文需要C语言基础掌握指针、结构体、动态内存分配与断言。顺序表概念讲解线性表线性表的定义线性表是最基本、最简单、也是最常用的一种数据结构。线性表*linear list*是数据结构的一种一个线性表是n个具有相同特性的数据元素的有限序列。线性表中数据元素之间的关系是一对一的关系即除了第一个和最后一个数据元素之外其它数据元素都是首尾相接的注意这句话只适用大部分线性表而不是全部。比如循环链表逻辑层次上也是一种线性表存储层次上属于链式存储但是把最后一个数据元素的尾指针指向了首位结点。定义很抽象简单来说就像是相同类型的数据排成一路纵队不可以出现并排或分叉就像一条直线一样。只要满足逻辑结构是线性的这个条件就可以称其为线性表线性表包括顺序表、链表、栈、队列等。顺序表与数组顺序表的逻辑结构与物理结构顺序表的逻辑结构是线性的顺序表符合线性表的定义所以它的逻辑结构是线性的。顺序表的物理结构也是线性的顺序表的底层是由数组实现的数组是将相同类型的数据按地址从低到高的顺序连续存储在内存上所以数组的物理结构是线性的由此可知顺序表的物理结构也是线性的。数组的缺点顺序表的底层是由数组实现的那为什么不直接使用数组呢因为数组有以下两个缺点。数组大小固定无法改变这个缺点会让数组很不灵活因为你写程序时是没办法预测将来要存储多少数据数组创建的太小会存储不下过多的数据创建的太大会造成空间浪费。无法知晓数组里面存储多少有效数据增删查改操作全都依赖于这个信息在讲解这些具体功能的时候你自然就理解了。顺序表是装修过的数组顺序表就是为了解决数组缺点而诞生的它通过以下两个方式解决。在堆上创建数组以int arr[5] {0};这种方式创建数组本质是在栈上创建数组我们没办法像操作堆一样自由的操作栈这是数组大小无法改变的根本原因所以我们就可以直接在堆上创建数组以达到自由改变数组大小的目的。存储数组的必要信息我们可以将数组的有效数据个数用整型size存储在进行增删查改等操作后实时更新size然后将数组的地址与大小分别用指针arr与整型capacity存储之所以存储数组的地址和大小是因为在堆上创建数组虽然更自由但是也更加操心需要我们自己记住数组的大小与地址后续讲解具体的代码你就会理解为何这么做。顺序表分为静态顺序表与动态顺序表静态顺序表不太常用将不会详细讲解所以本文默认顺序表为动态顺序表。顺序表代码讲解定义顺序表结构体顺序表的三个必要信息分别是arr、size、capacity三个同属顺序表的不同的信息需要用结构体进行储存所以定义结构体代码如下。首先将int重命名为SLTDataType这是为了方便更改存储的数据类型在更改类型时只需更改这行代码中的int即可改变后续所有SLTDataType类型的含义。//存储数据类型---inttypedefintSLTDataType;动态顺序表结构体成员是那三个必要信息这里将struct SeqList重命名为SL是缩短名字方便使用。//动态顺序表typedefstructSeqList{SLTDataType*arr;//顺序表地址intsize;//有效数据个数intcapacity;//空间大小}SL;静态顺序表大小无法改变只需存储定长数组与有效数据个数。//静态顺序表typedefstructSeqList{SLTDataType arr[100];//定长数组intsize;//有效数据个数}SL;顺序表初始化如果在创建结构体变量之后不对它进行初始化那么结构体成员都会是随机值。//初始化顺序表voidSLInit(SL*pa){assert(pa);pa-arrNULL;pa-sizepa-capacity0;return;}assert(pa);是断言防止pa为空指针因为空指针解引用属于未定义行为会出错。pa-arr NULL;将顺序表指针初始化为NULL表示此时并未申请空间。pa-size pa-capacity 0;有效数据个数与空间大小均初始化为0。顺序表扩容扩容函数就是控制顺序表增大的功能函数这里也能体现出顺序表在堆上申请空间的具体方式。//检查并扩容顺序表容量voidSLCheckCapacity(SL*pa){assert(pa);//判断是否增容if(pa-sizepa-capacity){//2倍增容intnewcapacity(pa-capacity0)?4:(pa-capacity*2);SLTDataType*temp(SLTDataType*)realloc(pa-arr,newcapacity*sizeof(SLTDataType));//判断申请是否成功if(tempNULL){perror(realloc fail !);exit(1);}pa-arrtemp;pa-capacitynewcapacity;}return;}if (pa-size pa-capacity)当sizecapacity时顺序表的空间足够无需扩容。当sizecapacity时顺序表空间已满进行扩容操作。int newcapacity (pa-capacity 0) ? 4 : (pa-capacity * 2);频繁的扩容会降低程序的性能所以需要倍数扩容至于进行具体几倍扩容则需要你自己权衡倍数越大扩容的次数就越少但是空间的浪费也更多。如果是首次创建顺序表就直接申请4个单位的空间。SLTDataType* temp (SLTDataType*)realloc(pa-arr, newcapacity * sizeof(SLTDataType));用realloc函数申请空间一定不要用arr存储新地址因为一旦空间申请失败不仅没法扩容还会造成原先地址的丢失。newcapacity并不是指新空间的大小而是指新空间能存储的数据个数所以真正的空间大小是newcapacity * sizeof(SLTDataType)需要乘以单个数据的大小。如果申请失败就直接结束程序。//判断申请是否成功if(tempNULL){perror(realloc fail !);exit(1);}更新结构体里面的信息。pa-arrtemp;pa-capacitynewcapacity;顺序表尾部插入元素顺序表可以通过尾插函数在已有的有效元素序列尾部插入元素。//顺序表尾部插入元素voidSLPushBack(SL*pa,SLTDataType x){assert(pa);//增容SLCheckCapacity(pa);//在尾部插入pa-arr[pa-size]x;return;}assert(pa);断言防止传入空指针。既然是插入元素那一定要进行SLCheckCapacity(pa);扩容操作。pa-arr[pa-size] x;直接在顺序表size下标位置插入元素然后size更新结构体信息。顺序表尾部插入元素的时间复杂度为O(1)。顺序表头部插入元素顺序表可以通过头插函数在已有的有效元素序列的头部插入元素头插操作相较于尾插更复杂。//顺序表头部插入元素voidSLPushFront(SL*pa,SLTDataType x){assert(pa);//增容SLCheckCapacity(pa);//在头部插入for(intipa-size;i0;i--){pa-arr[i]pa-arr[i-1];}pa-arr[0]x;pa-size;return;}assert(pa);断言防止传入空指针。SLCheckCapacity(pa);进行扩容操作。for (int i pa-size; i 0; i--)在头部插入元素不能像尾插一样直接插入需要先将所有元素整体后移一位腾出首元素空间并且一定要先挪后面的元素再挪前面的元素。先挪动前面的元素再挪后面的元素以这种方法移动元素会将所有元素全部被a覆盖掉为了插入一个元素而丢掉几乎所有数据这代价未免有点太大了。先挪动后面的元素再挪前面的元素这才是正确的移动方法最后在首元素位置插入元素即可。pa-arr[0] x;pa-size;更新结构体信息。顺序表头部插入元素的时间复杂度为O(n)。顺序表尾部删除元素尾删的逻辑基本与尾插相同。//顺序表尾部删除元素voidSLPopBack(SL*pa){assert(papa-size);//在尾部删除pa-size--;return;}assert(pa pa-size);防止传入空指针并且要防止顺序表为空空顺序表没法删除数据。直接pa-size--;即可完成删除这是因为删除数据的本质不是把数据销毁而是允许它所在的空间被使用当我们再次插入数据时自然就把被删除的数据覆盖掉了。顺序表尾部删除元素的时间复杂度为O(1)。顺序表头部删除元素同样顺序表头删的逻辑与头插也很像但是有差别不要记混。//顺序表头部删除元素voidSLPopFront(SL*pa){assert(papa-size);//在头部删除for(inti0;ipa-size-1;i){pa-arr[i]pa-arr[i1];}pa-size--;return;}assert(pa pa-size);防止传入空指针并且要防止顺序表为空。for (int i 0; i pa-size - 1; i)在头部删除数据要将所有元素整体向前移动一位和头插不同的是要先挪前面的元素再挪后面的元素。先挪后面的元素再挪前面的元素先挪前面的元素再挪后面的元素头删挪动顺序与头插相反但是原因相同看图可以发现a也并没有被直接销毁而是被覆盖和尾插一样。通过这里可以发现删除数据的本质是允许它所在的空间被再次写入数据而不是将它所在空间的数据直接删除。pa-size--;更新顺序表结构体信息。顺序表头部删除元素的时间复杂度为O(n)。查找顺序表元素它的作用是查找顺序表内是否有我们要找的元素它一般与在顺序表指定位置插入元素功能配合使用。//查找元素返回下标intSLFind(SL*pa,SLTDataType x){assert(pa);//遍历寻找for(inti0;ipa-size;i){if(pa-arr[i]x){returni;}}return-1;}assert(pa);断言防止传入空指针。for (int i 0; i pa-size; i)遍历查找顺序表的所有元素。arr[i]目标元素时直接返回当前元素的下标。遍历所有元素后没有找到目标元素则返回-1代表没找到。查找顺序表元素的时间复杂度为O(n)。顺序表指定位置插入元素它的代码逻辑与头插基本相同。//在指定下标位置插入元素voidSLInsert(SL*pa,intpos,SLTDataType x){assert(pa(pos0pospa-size));//增容SLCheckCapacity(pa);//遍历后移for(intipa-size;ipos;i--){pa-arr[i]pa-arr[i-1];}pa-arr[pos]x;pa-size;return;}assert(pa (pos 0 pos pa-size));防止传入空指针并且要保证pos是顺序表的有效下标这就是为什么查找函数在找不到目标元素的时候返回-1因为查找函数的返回值一般直接作为指插函数的参数进行使用。SLCheckCapacity(pa);对顺序表进行扩容。for (int i pa-size; i pos; i--)和头插一个逻辑只不过空出的不再是首元素位置而是指定位置同样要先挪后面再挪前面如果你看懂了头插与头删的流程图自然就能够理解指插的逻辑。pa-arr[pos] x;pa-size;更新结构体信息。顺序表指定位置插入元素的时间复杂度为O(N)。销毁顺序表顺序表是在堆上申请空间创建的需要我们自己回收空间。//销毁顺序表voidSLDestroy(SL*pa){assert(pa);//销毁free(pa-arr);pa-arrNULL;pa-sizepa-capacity0;return;}assert(pa);防止传入空指针。free(pa-arr);回收顺序表空间。pa-arr NULL;pa-size pa-capacity 0;更新结构体信息。代码练习本文讲解的顺序表代码还有以下功能没有实现//在指定下标后插入元素voidSLInsertAfter(SL*pa,intpos,SLTDataType x);//删除指定下标位置元素voidSLErase(SL*pa,intpos);独立的去实现本文讲解的所有代码并画流程图。本文未讲解的两个函数代码逻辑与前面讲解过的代码逻辑基本相同容易踩的坑前面也有讲解如果你已经学会本文的内容那么这两个功能你就可以独立实现。总结函数名功能时间复杂度SLPushBack顺序表尾部插入元素O(1)SLPushFront顺序表头部插入元素O(n)SLPopBack顺序表尾部删除元素O(1)SLPopFront顺序表头部删除元素O(n)SLFind查找顺序表元素O(n)SLInsert顺序表指定位置插入元素O(n)完整源码已上传至 GitHubSean-Next/c-data-structure-learning
延伸阅读

更多相关文章

2026/10/24 9:08:17

大模型越狱攻防:从安全测试到本地部署加固实践

这次我们来看一个最近在大模型圈子里绕不开的话题:大模型越狱。注意,这里说的不是 iOS 那个越狱,而是大模型安全测试里常说的 Jailbreak。简单讲,就是通过构造特定输入提示词,尝试绕过模型自身的安全对齐策略&#xff…

2026/10/21 9:30:51

水母堵塞核电站冷源:从滤网差压到反应堆强制停堆的完整链路

在沿海核电站的运行记录中,因为设备故障、电网波动或极端天气导致反应堆停运并不罕见,但“水母导致三座反应堆强制关闭”这种事件,第一次看到时很容易被当作一则猎奇新闻。实际上,它揭示的是核电和大型滨海工业设施中一个非常现实…

2026/10/24 20:59:28

自然语言界面只保留了表单五件事中的一件,工程真相是什么?

“Forms did five things. Natural language kept one”这句话,我第一次看到时觉得有点绕,但结合最近在做的对话式表单、AI Agent 录入类项目再看,突然就通了。传统表单页面里,一个录入表单通常要承担“字段定义、用户引导、取值约…

2026/10/24 23:52:21

MyBatisPlus批量插入性能优化与实现方案

1. 为什么需要自己实现MyBatisPlus批量插入MyBatisPlus作为MyBatis的增强工具,虽然提供了基础的CRUD操作,但在批量插入场景下存在几个明显的痛点。首先是性能问题,默认的saveBatch()方法实际上是通过for循环执行单条INSERT语句,当…

2026/10/24 23:52:21

STM32N6启动失败根源:FSBL签名头与BootROM信任链解析

简介:本资源是一份面向嵌入式开发工程师与ARM架构技术爱好者的STM32N6芯片启动机制深度解析文档,聚焦FSBL(第一阶段引导加载程序)的原理、流程与工程实践,解决开发者在新平台启动适配、安全签名及XIP/Load&Run模式…

2026/10/24 23:52:21

UCIe电气层分析怎么做?Chiplet集成中的链路预算与精简仿真流程

做Chiplet集成绕不开UCIe,而UCIe里最容易被低估的就是电气层。协议层写清楚了你用什么报文通信,但芯片要真正跑起来,差分线、凸块、基板走线、转接板过孔,每一个环节都是模拟信号问题。我前段时间在推进一个多芯粒封装的模拟项目X…

2026/10/24 23:47:21

Python爬虫抓热搜榜前50条:JSON接口清洗与Excel自动化落盘

简介:围绕微博热搜采集与Excel落盘的Python爬虫练习包,面向希望掌握网页请求、HTML解析和表格导出的入门到进阶学习者。它聚焦微博前50条热门话题的抓取与整理,借助requests获取页面、BeautifulSoup定位热搜列表并提取标题与链接,…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑