第31篇 数据结构入门:顺序表

发布时间:2026/7/12 1:18:15

第31篇 数据结构入门:顺序表
数据结构入门顺序表与链表的深度解析在编程的世界里如何高效地存储和管理数据是核心问题。线性表Linear List作为最基础、最常用的数据结构是我们必须掌握的第一课。线性表在逻辑上是一条连续的直线但在物理存储上却分为两大流派顺序表和链表。今天我们就来深入剖析顺序表。文章目录1. 静态顺序表 vs 动态顺序表第一部分顺序表代码实现1. 项目文件划分2. 类型重定义与结构体优化3. 初始化与扩容机制4. 增删改查接口实现第二部分LeetCode 经典例题实战1. 移除元素 (LeetCode 27)2. 删除有序数组中的重复项 (LeetCode 26)3. 合并两个有序数组 (LeetCode 88)3. 顺序表的痛点与展望什么是顺序表顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构。简单来说它的底层就是数组。你可以把它想象成一家米其林餐厅数组就像是原材料比如炒西蓝花。顺序表则是对数组进行了封装增加了“摆盘”和“服务”即增删改查接口变成了一道精致的菜比如绿野仙踪。1. 静态顺序表 vs 动态顺序表静态顺序表使用定长数组存储。缺陷在于空间给少了不够用给多了造成浪费不够灵活。动态顺序表按需申请空间。这是我们在实际开发中更常用的形式。它包含三个核心要素a指向数据的指针。size当前有效数据的个数。capacity当前空间的总容量。// 动态顺序表结构体定义typedefstructSeqList{SLDataType*a;// 指向动态开辟的数组intsize;// 有效数据个数intcapacity;// 空间容量}SL;正如前文所述顺序表是对数组进行了封装增加了增删改查的接口。接下来我们就来一步步实现这些功能。第一部分顺序表代码实现1. 项目文件划分为了方便管理和维护我们将代码分为三个文件SL.h公共接口声明函数、定义结构体。SL.c具体实现#include SL.h编写函数体。test.c测试逻辑#include SL.h调用函数进行验证。2. 类型重定义与结构体优化为了避免后续修改数据类型时产生大量冗余工作我们使用typedef对数据类型和结构体进行重命名。// 将元素类型重命名方便后续统一修改typedefintSLDataType;// 动态顺序表结构体定义typedefstructSeqList{SLDataType*a;// 指向动态开辟的数组intsize;// 有效数据个数intcapacity;// 空间容量}SL;3. 初始化与扩容机制由于后续操作需要修改结构体内部的数据因此传参一律采用传址调用指针。同时为了防范野指针我们引入assert.h进行断言检查。初始化函数voidSLinit(SL*ps){assert(ps);ps-aNULL;ps-size0;ps-capacity0;}扩容函数每次进行插入操作前都需要检查空间是否充足。扩容策略为若初始容量为0则默认开辟4个空间否则扩大为原来的2倍。同时必须妥善处理realloc可能返回NULL的隐患防止原数据丢失。voidSLdilat(SL*p){assert(p);intnewcapacity(p-capacity0?4:p-capacity*2);// 使用临时指针接收 realloc 的返回值防止扩容失败导致原指针丢失SLDataType*tmp(SLDataType*)realloc(p-a,newcapacity*sizeof(SLDataType));if(tmpNULL){perror(realloc fail : );return;}p-atmp;p-capacitynewcapacity;}4. 增删改查接口实现尾部增加元素voidSLtailadd(SL*p,SLDataType x){assert(p);if(p-sizep-capacity){SLdilat(p);}p-a[p-size]x;// 后置使得代码更加简洁美观}尾部删除元素删除操作只需将有效数据个数size减 1 即可无需真正清除内存中的数据。voidSLtaildel(SL*p){assert(p);if(p-size0)return;p-size--;}头部增加元素需要将原有数据整体向后挪动一位再在首位置赋值。voidSLheadadd(SL*p,SLDataType x){assert(p);if(p-sizep-capacity){SLdilat(p);}for(intip-size;i0;i--){p-a[i]p-a[i-1];}p-a[0]x;p-size;}头部删除元素将第二个元素开始的数据依次向前覆盖最后更新size。voidSLheaddel(SL*p){assert(p);if(p-size0)return;for(inti0;ip-size-1;i){p-a[i]p-a[i1];}p-size--;}查找指定数据遍历数组寻找目标数据找到返回其索引找不到返回 -1。intSLfind(SL*p,SLDataType x){assert(p);for(inti0;ip-size;i){if(p-a[i]x)returni;}return-1;}在指定位置前插入数据结合查找函数先定位索引再将目标位置及之后的数据整体后移。voidSLfixadd(SL*p,SLDataType x,intpos){assert(p);if(p-sizep-capacity){SLdilat(p);}for(intip-size;ipos;i--){p-a[i]p-a[i-1];}p-a[pos]x;p-size;}打印函数用于测试voidSLprint(SL*p){assert(p);for(inti0;ip-size;i){printf(%d\t,p-a[i]);}printf(\nsize: %d\tcapacity: %d\n,p-size,p-capacity);}测试代码示例#includeSL.hintmain(){SL s;SLinit(s);SLtailadd(s,8);SLprint(s);SLheadadd(s,6);SLprint(s);printf(请输入你指定的数字);SLDataType i;scanf(%d,i);intposSLfind(s,i);if(pos-1){printf(你提供的数值不在该顺序表里面\n);}else{printf(请输入你想要插入的数字);SLDataType j;scanf(%d,j);SLfixadd(s,j,pos);SLprint(s);}return0;}第二部分LeetCode 经典例题实战掌握了顺序表的基础操作后我们通过三道经典题目来巩固“双指针法”这一核心思想。1. 移除元素 (LeetCode 27)思路使用快慢双指针。sou指针负责遍历数组当遇到不等于val的值时将其赋值给des指针指向的位置然后des后移。最终des与起始位置的差值即为新数组的长度。intremoveElement(int*nums,intnumsSize,intval){int*sounums;int*desnums;for(inti0;inumsSize;i){if(*sou!val){*des*sou;des;}sou;}returndes-nums;}2. 删除有序数组中的重复项 (LeetCode 26)思路同样使用双指针。des初始在首位sou从第二位开始遍历。当sou指向的值与des不同时des先自增再接收sou的值。注意由于是先自增再赋值最终返回的长度需要des - nums 1或者在循环外处理。intremoveDuplicates(int*nums,intnumsSize){if(numsSize0)return0;int*desnums;int*sounums1;for(inti1;inumsSize;i){if(*sou!*des){des;*des*sou;}sou;}returndes-nums1;}3. 合并两个有序数组 (LeetCode 88)思路为了避免从头开始比较带来的数据搬移开销我们采用“逆向双指针”法。从两个数组的有效尾部开始比较将较大的元素放到nums1的最终尾部依次向前填充。voidmerge(int*nums1,intnums1Size,intm,int*nums2,intnums2Size,intn){intendnums1Size-1;while(m0n0){if(nums1[m-1]nums2[n-1]){nums1[end--]nums1[m-1];m--;}else{nums1[end--]nums2[n-1];n--;}}// 如果 nums2 还有剩余直接拷贝到 nums1 前面while(n0){nums1[end--]nums2[n-1];n--;}}3. 顺序表的痛点与展望虽然顺序表支持随机访问下标访问时间复杂度O ( 1 ) O(1)O(1)但它也有明显的短板头部/中间插入删除效率低需要搬移大量数据时间复杂度为O ( N ) O(N)O(N)。扩容成本高当空间不足时需要申请新空间、拷贝数据、释放旧空间。空间浪费扩容通常呈2倍增长如果插入少量数据后不再操作剩余空间会被白白浪费。那么有没有办法解决这些痛点呢有的这就需要用到链表了。不过那就是我们下一篇博客要探讨的内容了敬请期待

相关新闻

如何完整备份微信聊天记录:WeChatMsg数据自主管理实用指南

如何完整备份微信聊天记录:WeChatMsg数据自主管理实用指南

2026/7/11 19:51:57

如何完整备份微信聊天记录:WeChatMsg数据自主管理实用指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…

Supabase 数据库介绍:开源 Firebase 替代方案

Supabase 数据库介绍:开源 Firebase 替代方案

2026/7/10 22:46:20

1. 什么是 Supabase?Supabase 是一个开源的 Firebase 替代方案,提供了一套完整的后端即服务(BaaS)解决方案。它基于 PostgreSQL 数据库构建,并提供了实时订阅、身份验证、存储、函数等丰富的功能。Supabase 的核心目标…

【HarmonyOS NEXT】error: failed to install bundle. code:9568322...

【HarmonyOS NEXT】error: failed to install bundle. code:9568322...

2026/7/11 7:42:44

🎯 核心原因一:手动签名配置了发布证书(Release Profile)这是最常见的原因之一。发布证书签名的应用,无法直接通过hdc命令安装到真机进行调试。现象:你按照文档配置了生产环境的Profile,设备也添…

西安软件开发服务商分层调研报告:中型合规服务商迎来发展红利

西安软件开发服务商分层调研报告:中型合规服务商迎来发展红利

2026/7/12 1:13:44

第一章 行业热点深度解析:市场两极分化,中型合规服务商迎来发展红利7 月 11 日,行业媒体发布服务商分层调研报告,软件开发赛道形成十分清晰的三级格局,行业淘汰节奏加快,不同类型开发团队的生存空间出现明显…

Bellman-Ford 算法 C++ 实现:负权图最短路径与负权环检测 5 步详解

Bellman-Ford 算法 C++ 实现:负权图最短路径与负权环检测 5 步详解

2026/7/12 1:13:44

Bellman-Ford 算法 C 实现:负权图最短路径与负权环检测 5 步详解1. 算法核心思想与适用场景Bellman-Ford 算法是图论中解决单源最短路径问题的经典算法,由 Richard Bellman 和 Lester Ford 在 20 世纪 50 年代提出。与 Dijkstra 算法相比,它的…

NBM7100A与PIC18F4685在低功耗设备中的能量管理方案

NBM7100A与PIC18F4685在低功耗设备中的能量管理方案

2026/7/12 1:13:44

1. 项目背景与核心挑战在物联网和低功耗设备领域,如何最大化利用不可充电的初级电池(如锂锰电池)的能量一直是个棘手问题。这类电池在突发电流负载下表现尤为脆弱——电压骤降会导致设备意外重启,而电池内剩余的大量能量却无法被有…

GodotSteam游戏性能优化实战:10个技巧解决内存与网络瓶颈

GodotSteam游戏性能优化实战:10个技巧解决内存与网络瓶颈

2026/7/12 1:13:44

1. 项目概述:为什么GodotSteam项目需要性能优化?如果你正在用Godot引擎开发Steam平台的游戏,尤其是带有多人联机功能的项目,那么“性能”这个词,大概率已经让你头疼过不止一次了。我经历过不止一个项目,在开…

Node.js 核心模块实战:path 与 fs

Node.js 核心模块实战:path 与 fs

2026/7/12 1:13:44

从路径拼接到文件读写,掌握 Node.js 两大核心模块,并理解 JS 异步编程的完整进化链。📌 本文结构 path 模块(路径处理) → fs 模块(文件系统)→ 同步 vs 异步 → 异步进化史(回调 →…

基于Antigravity与Gemini构建智能跑步教练系统的实践指南

基于Antigravity与Gemini构建智能跑步教练系统的实践指南

2026/7/12 1:03:44

1. 先搞清楚这个项目到底解决什么实际问题这个项目标题看起来有点炫,但核心其实很直接:用 Antigravity 和 Gemini 这两个工具组合起来,做一个能指导跑步训练的 AI 教练。不是那种只会说“加油”的语音助手,而是能分析你的跑步数据…

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

2026/7/12 0:03:42

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为Unity游戏社区中最成熟的文本翻译解决方…

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

2026/7/12 0:03:42

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持 【免费下载链接】raspberrypi-kernel It provides openEuler kernel source for Raspberry Pi 项目地址: https://gitcode.com/openeuler/raspberrypi-kernel 前往项目官网免费下载&…

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

2026/7/12 0:03:42

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载:…

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

2026/7/12 0:03:42

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为Unity游戏社区中最成熟的文本翻译解决方…

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

2026/7/12 0:03:42

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持 【免费下载链接】raspberrypi-kernel It provides openEuler kernel source for Raspberry Pi 项目地址: https://gitcode.com/openeuler/raspberrypi-kernel 前往项目官网免费下载&…

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

2026/7/12 0:03:42

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载:…