当前位置: 首页 > article >正文

顺序表完全指南:从原理到实现

引言在数据结构的学习中线性表是最基础也是最重要的数据结构之一。线性表是n个数据元素的有限序列这些元素具有相同的特性。线性表从存储结构上分为两种顺序表物理地址连续数组链表物理地址不连续逻辑上连续通过指针连接顺序表采用数组存储数据元素在内存中物理地址连续可以通过下标直接访问任意元素时间复杂度为O(1)。今天我们将从零开始实现一个完整的顺序表涵盖初始化、增删改查、反转、合并等核心操作。第一部分顺序表的基本概念一、什么是顺序表顺序表是用一段连续的物理内存来存储数据元素的线性结构。在C语言中通常使用数组来实现。二、顺序表结构体设计#include stdio.h #include assert.h #include stdlib.h #include string.h #include stdbool.h #define MAX_SIZE 100 // 顺序表结构体 struct SeqList { int arr[MAX_SIZE]; // 存储数据的数组固定大小 int seqsize; // 当前元素个数 int capacity; // 最大容量 };结构体成员说明成员类型作用arrint数组存储实际数据seqsizeint记录当前有多少个有效元素capacityint记录数组的最大容量第二部分顺序表的基础操作一、初始化原理将顺序表的元素个数设为0容量设为最大值。void InitSeqList(struct SeqList* ps) { assert(ps ! NULL); // 断言确保指针不为空 ps-capacity MAX_SIZE; // 设置容量 ps-seqsize 0; // 初始没有元素 }二、判空与判满判空用于判断顺序表是否为空在删除操作前需要检查。bool IsEmpty(struct SeqList* ps) { assert(ps ! NULL); return ps-seqsize 0; }判满用于判断顺序表是否已满在插入操作前需要检查。bool IsFull(struct SeqList* ps) { assert(ps ! NULL); return ps-seqsize ps-capacity; }三、打印顺序表void Print(struct SeqList* ps) { assert(ps ! NULL); for (int i 0; i ps-seqsize; i) { printf(%d , ps-arr[i]); } printf(\n); }第三部分插入操作一、尾插在末尾插入原理直接在seqsize位置放入新元素因为数组下标从0开始seqsize正好是下一个空闲位置。// 时间复杂度O(1) bool Insert_Back(struct SeqList* ps, int val) { assert(ps ! NULL); if (IsFull(ps)) return false; ps-arr[ps-seqsize] val; ps-seqsize; return true; }示意图二、头插在头部插入原理将所有元素向后移动一位然后将新元素放在第一个位置。// 时间复杂度O(n) bool Insert_Front(struct SeqList* ps, int val) { assert(ps ! NULL); if (IsFull(ps)) return false; // 将arr[0]到arr[seqsize-1]整体向后移动1位 memmove(ps-arr 1, ps-arr, sizeof(ps-arr[0]) * ps-seqsize); ps-arr[0] val; ps-seqsize; return true; }示意图三、按下标插入原理将index位置含之后的元素向后移动一位然后在index位置放入新元素。// 时间复杂度O(n) bool Insert_Index(struct SeqList* ps, int val, int index) { assert(ps ! NULL); if (IsFull(ps)) return false; if (index ps-seqsize || index 0) return false; // 将index及之后的元素向后移动 memmove(ps-arr index 1, ps-arr index, sizeof(ps-arr[0]) * (ps-seqsize - index)); ps-arr[index] val; ps-seqsize; return true; }四、按位置插入位置从1开始原理与按下标插入类似但位置从1开始计数更符合人的习惯。位置pos对应的下标是pos-1。// 时间复杂度O(n) bool Insert_Pos(struct SeqList* ps, int val, int pos) { assert(ps ! NULL); if (IsFull(ps)) return false; if (pos 1 || pos ps-seqsize 1) return false; // pos-1 是实际的下标 memmove(ps-arr pos, ps-arr pos - 1, sizeof(ps-arr[0]) * (ps-seqsize - pos 1)); ps-arr[pos - 1] val; ps-seqsize; return true; }第四部分删除操作一、尾删在末尾删除原理只需将seqsize减1即可原来最后一个元素变为“无效”下次插入会覆盖。// 时间复杂度O(1) bool Pop_Back(struct SeqList* ps) { assert(ps ! NULL); if (IsEmpty(ps)) return false; ps-seqsize--; return true; }二、头删删除头部原理将第二个元素开始的所有元素向前移动一位覆盖第一个元素。// 时间复杂度O(n) bool Pop_Front(struct SeqList* ps) { assert(ps ! NULL); if (IsEmpty(ps)) return false; memmove(ps-arr, ps-arr 1, (--ps-seqsize) * sizeof(int)); return true; }示意图三、按下标删除// 时间复杂度O(n) bool Pop_Index(struct SeqList* ps, int index) { assert(ps ! NULL); if (IsEmpty(ps)) return false; if (index 0 || index ps-seqsize) return false; memmove(ps-arr index, ps-arr index 1, sizeof(int) * (ps-seqsize - index - 1)); ps-seqsize--; return true; }四、按位置删除// 时间复杂度O(n) bool Pop_Pos(struct SeqList* ps, int pos) { assert(ps ! NULL); if (IsEmpty(ps)) return false; if (pos 1 || pos ps-seqsize) return false; memmove(ps-arr pos - 1, ps-arr pos, sizeof(int) * (ps-seqsize - pos)); ps-seqsize--; return true; }五、按值删除删除所有匹配的元素原理使用双指针法将所有不等于val的元素保留下来覆盖到数组前面。// 方法1遍历删除边删边移动 bool Pop_Val1(struct SeqList* ps, int val) { assert(ps ! NULL); if (IsEmpty(ps)) return false; bool found false; for (int i 0; i ps-seqsize; i) { if (val ps-arr[i]) { // 删除找到的元素后面的元素前移 memmove(ps-arr i, ps-arr i 1, sizeof(int) * (--ps-seqsize - i)); i--; // 继续检查当前位置原i1已移到i found true; } } return found; } // 方法2双指针法更高效一次遍历完成 bool Pop_Val2(struct SeqList* ps, int val) { assert(ps ! NULL); if (IsEmpty(ps)) return false; int j 0; // 新数组的写入位置 for (int i 0; i ps-seqsize; i) { if (ps-arr[i] ! val) { ps-arr[j] ps-arr[i]; } } // 如果所有元素都被保留说明没有找到val if (j ps-seqsize) return false; ps-seqsize j; return true; }双指针法示意图第五部分顺序表的高级操作一、交换函数辅助函数void swap(int* a, int* b) { // 不使用临时变量的交换加减法 *a *a *b; *b *a - *b; *a *a - *b; // 注意此方法可能导致溢出生产环境中使用临时变量更安全 } // 更安全的版本 void swap_safe(int* a, int* b) { int temp *a; *a *b; *b temp; }二、反转顺序表原理双指针法从两端向中间交换元素。// 时间复杂度O(n) void Reverse_SeqList(struct SeqList* ps) { assert(ps ! NULL); if (IsEmpty(ps)) return; int left 0; int right ps-seqsize - 1; while (left right) { swap(ps-arr[left], ps-arr[right]); left; right--; } }示意图三、顺序表相加大数加法原理将两个顺序表当作数字每个元素是一位数字相加得到新的顺序表。// 类似大数加法的思路 void Add_SeqList1_SeqList2(struct SeqList* ps1, struct SeqList* ps2, struct SeqList* result) { assert(ps1 ! NULL ps2 ! NULL result ! NULL); // 如果两个都为空直接返回 if (IsEmpty(ps1) IsEmpty(ps2)) return; // 如果其中一个为空直接复制另一个 if (IsEmpty(ps1)) { memmove(result-arr, ps2-arr, ps2-seqsize * sizeof(ps2-arr[0])); result-seqsize ps2-seqsize; return; } if (IsEmpty(ps2)) { memmove(result-arr, ps1-arr, ps1-seqsize * sizeof(ps1-arr[0])); result-seqsize ps1-seqsize; return; } int carry 0; // 进位 int i ps1-seqsize - 1; int j ps2-seqsize - 1; // 从最低位数组末尾开始相加 while (i 0 || j 0 || carry) { if (i 0) carry ps1-arr[i--]; if (j 0) carry ps2-arr[j--]; // 头插结果保证顺序正确 Insert_Front(result, carry % 10); carry / 10; } }示例ps1: [1, 2, 3] → 数字 123 ps2: [9, 8, 7] → 数字 987 相加123 987 1110 结果[1, 1, 1, 0]第六部分综合测试int main() { struct SeqList s1, s2, result; // 初始化顺序表1 InitSeqList(s1); Insert_Front(s1, 1); Insert_Front(s1, 2); Insert_Front(s1, 3); printf(顺序表1: ); Print(s1); // 3 2 1 // 初始化顺序表2 InitSeqList(s2); Insert_Front(s2, 9); Insert_Front(s2, 8); Insert_Front(s2, 7); printf(顺序表2: ); Print(s2); // 7 8 9 // 顺序表相加 InitSeqList(result); Add_SeqList1_SeqList2(s1, s2, result); printf(相加结果: ); Print(result); // 1 1 1 0 // 反转测试 Reverse_SeqList(result); printf(反转后: ); Print(result); return 0; }第七部分顺序表操作复杂度总结操作时间复杂度说明尾插O(1)直接写入头插O(n)需要移动所有元素按位置插入O(n)需要移动插入位置后的元素尾删O(1)直接减小size头删O(n)需要移动所有元素按位置删除O(n)需要移动删除位置后的元素按值删除O(n)需要遍历查找反转O(n)双指针交换按值查找O(n)遍历查找按下标访问O(1)数组直接索引总结一、顺序表核心要点概念说明物理存储连续内存数组逻辑结构线性结构访问方式下标访问 O(1)插入/删除尾部O(1)中间/头部O(n)容量限制固定大小静态或可扩容动态二、常用操作速查表操作函数名关键点初始化InitSeqList设置seqsize0判空IsEmptyseqsize 0判满IsFullseqsize capacity尾插Insert_Back直接放入seqsize位置头插Insert_Front整体后移再放头部尾删Pop_Back直接seqsize--头删Pop_Front整体前移按值删除Pop_Val双指针法高效反转Reverse_SeqList双指针交换三、memmove vs memcpy函数特点适用场景memmove支持内存重叠元素移动源和目标重叠memcpy不支持内存重叠不重叠的拷贝本文详细介绍了顺序表的实现涵盖了初始化、判空、判满各种插入和删除操作头、尾、按位置、按值反转和加法等高级操作每个操作的时间复杂度分析顺序表是数据结构学习的起点理解其原理对于后续学习链表、栈、队列等数据结构至关重要。学习建议自己动手实现一遍所有函数理解每个操作的时间复杂度注意边界条件的处理学会使用断言assert进行参数检查

相关文章:

顺序表完全指南:从原理到实现

引言在数据结构的学习中,线性表是最基础也是最重要的数据结构之一。线性表是n个数据元素的有限序列,这些元素具有相同的特性。线性表从存储结构上分为两种:顺序表:物理地址连续(数组)链表:物理地…...

避坑指南:Linux下用Ollama+MaxKB搭建私有知识库,我踩过的那些GPU和网络坑

避坑指南:Linux下用OllamaMaxKB搭建私有知识库,我踩过的那些GPU和网络坑 在Linux环境下搭建私有知识库,尤其是结合Ollama和MaxKB这样的工具,听起来是个很酷的主意。但说实话,这个过程远没有教程里写的那么一帆风顺。作…...

【限时公开】某金融级Java服务网格生产规范V2.3(含mTLS双向认证配置模板、策略白名单清单、熔断阈值黄金比例)

更多请点击: https://intelliparadigm.com 第一章:Java服务网格的核心架构与金融级合规要求 服务网格在Java生态中的定位演进 传统Java微服务依赖Spring Cloud Netflix组件实现服务发现、熔断与路由,但其侵入式SDK与生命周期耦合难以满足金…...

智能座舱“卡顿”是谁的锅?一次性能与兼容性测试实战复盘(含工具链)

智能座舱“卡顿”是谁的锅?一次性能与兼容性测试实战复盘(含工具链) 当用户按下启动按钮,期待的是丝滑流畅的交互体验,而非令人烦躁的延迟与卡顿。智能座舱作为人车交互的核心界面,其性能表现直接影响用户对…...

10个Gemini3.1Pro办公模板,效率翻倍

现在很多人都知道 AI 能提升办公效率,但真正用起来时,常常卡在第一步: 不知道怎么问、不会写提示词、模型输出结果不稳定。其实,办公场景里最实用的 AI 用法,不是追求“很炫”的效果,而是把高频任务标准化。…...

别再让VIP日志拖慢仿真了!手把手教你用UVM精准控制Synopsys验证VIP的打印与检查

芯片验证效率革命:UVM与Synopsys VIP的日志优化实战指南 当SoC设计规模突破亿门级,验证工程师最常遇到的噩梦是什么?不是复杂的协议时序,不是刁钻的corner case,而是——仿真速度。特别是在回归测试阶段,那…...

DINOv2与SiT-B/2结合的图像生成优化技术

1. 项目背景与核心价值在计算机视觉领域,图像生成技术正经历着从传统GAN到扩散模型的范式转移。DINOv2作为Meta开源的视觉特征提取器,通过自监督学习实现了强大的图像表征能力;而SiT-B/2(Scalable Diffusion Transformer&#xff…...

AI智能体开发实战:基于agent-recipes构建可复现的智能体配方

1. 项目概述:当AI智能体遇上“菜谱”,一场关于可复现性的革命最近在GitHub上闲逛,发现了一个挺有意思的项目,叫agent-recipes。光看名字,你可能会联想到烹饪,但这里的“菜谱”可不是教你做菜,而…...

利用SAR图像相位信息的YOLOv10遥感舰船检测:从原理到实战完全指南

大家好,我最近在做一个遥感目标检测的项目,用的是SAR图像。说实话,踩了不少坑。最开始用的是普通光学图像那套思路,结果发现SAR图像的特性完全不一样。后来查阅了大量文献,发现很多人忽视了SAR图像的一个重要特性——相位信息。这篇文章我就把自己这段时间的心得、代码实现…...

JTAG技术解析:从原理到嵌入式调试实践

1. JTAG技术概述:从测试接口到调试利器JTAG(Joint Test Action Group)这个名词在工程师群体中早已超越了其原始含义,成为硬件测试和嵌入式调试的代名词。这项技术最初由联合测试行动小组在1980年代提出,后来被IEEE采纳…...

蓝河工具箱下载6.6最新版

🔧 蓝河工具箱 - 您的Android好帮手 下载地址:从夸克网盘下载 从UC网盘下载 📱 智能优化,简单操作,专业体验 欢太工具箱 玄戒工具箱 蓝河工具箱是一款专为vivo、iQOO用户打造的全面系统优化工具&#…...

如何快速掌握TQVaultAE:终极泰坦之旅装备管理完整指南

如何快速掌握TQVaultAE:终极泰坦之旅装备管理完整指南 【免费下载链接】TQVaultAE Extra bank space for Titan Quest Anniversary Edition 项目地址: https://gitcode.com/gh_mirrors/tq/TQVaultAE 你是否曾在《泰坦之旅》中为仓库爆满而烦恼?是…...

别再只用if-else了!用状态机优化你的STM32循迹小车代码,让逻辑更清晰

用状态机重构STM32循迹小车:告别if-else的工程化实践 当你的循迹小车第一次成功沿着黑线跑起来时,那种成就感无与伦比。但随着功能不断增加——十字路口识别、起跑线检测、障碍物避让——你会发现原本清晰的if-else结构正在变成一团乱麻。每次修改都可能…...

避坑指南:nRF52832 SAADC配置中的那些‘坑’——增益、参考电压与EasyDMA缓冲区设置详解

nRF52832 SAADC实战避坑手册:从参数配置到DMA优化的深度解析 在嵌入式开发中,模拟信号采集是连接物理世界与数字系统的关键桥梁。nRF52832的SAADC(Successive Approximation Analog-to-Digital Converter)模块因其集成度高、功耗低…...

从STC89C52到蓝牙芯片CC2541:揭秘那些‘披着MCU马甲’的SOC是如何诞生的

从STC89C52到蓝牙芯片CC2541:芯片定制化演进的商业逻辑与技术密码 在深圳华强北的某个电子市场柜台前,一位硬件工程师正对着两款芯片犹豫不决:左边是售价3.8元的STC89C52RC,右边是标价15元的CC2541蓝牙模块。这两颗看似毫无关联的…...

TrollInstallerX终极指南:如何在iOS 14.0-16.6.1设备上轻松安装TrollStore

TrollInstallerX终极指南:如何在iOS 14.0-16.6.1设备上轻松安装TrollStore 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX TrollInstallerX是一款专为iOS 14…...

.NET 9 AOT编译终极调优:6个MSBuild参数+3个RuntimeConfig.json隐藏开关,让边缘设备CPU占用直降67%

更多请点击: https://intelliparadigm.com 第一章:.NET 9 AOT编译与边缘计算场景适配性分析 .NET 9 引入了更成熟的原生 AOT(Ahead-of-Time)编译能力,显著降低启动延迟、内存占用和部署包体积,使其在资源…...

Windows HEIC缩略图插件:让你的电脑也能预览iPhone照片

Windows HEIC缩略图插件:让你的电脑也能预览iPhone照片 【免费下载链接】windows-heic-thumbnails Enable Windows Explorer to display thumbnails for HEIC/HEIF files 项目地址: https://gitcode.com/gh_mirrors/wi/windows-heic-thumbnails 你是否经常在…...

【ISO/IEC 14882:2027草案第12.8节权威解读】:为什么你的noexcept函数仍在抛异常?3类隐式异常路径正在绕过你的防护

更多请点击: https://intelliparadigm.com 第一章:C27异常处理安全增强配置的演进动因与标准定位 C27 将首次引入标准化的异常安全配置模型(Exception Safety Configuration Model, ESCM),旨在解决长期存在的跨编译器…...

QKeyMapper深度解析:从零开始构建专业级Windows按键映射系统

QKeyMapper深度解析:从零开始构建专业级Windows按键映射系统 【免费下载链接】QKeyMapper [按键映射工具] QKeyMapper,Qt开发Win10&Win11可用,不修改注册表、不需重新启动系统,可立即生效和停止。支持游戏手柄映射到键鼠&…...

静态反射不再纸上谈兵,C++27元数据驱动开发全链路解析,含AST遍历、属性注入与SFINAE-Free约束推导

更多请点击: https://intelliparadigm.com 第一章:静态反射元编程的范式跃迁 从运行时到编译期的认知重构 传统反射(如 Go 的 reflect 包或 Java 的 java.lang.Class)在运行时解析类型信息,带来显著性能开销与泛型…...

全链路压测的环境复杂性:网络架构、应用架构与性能影响因素全解析

一、为什么全链路压测的环境成本如此之高 全链路压测的高成本根源在于环境本身的复杂性。这种复杂性来自两个维度:线上网络结构的层级深度,以及应用架构的规模与迭代频率。理解这两个维度,是判断是否值得做线上压测、如何规划压测范围的前提。…...

Al Agent 企业应用30个落地案例拆解

2026年是场景建设大爆发的一年 以下是 100 个 AI Agent 的创新应用场景,覆盖教育、电商、医疗等多个行业 💡【深度研究】AI Agent赋能传统企业转型:30个智能体应用案例剖析 💡【实战指南】AI Agent商业案例精选,助你…...

一篇不错的自进化Agents最新系统性综述

近期,厦门大学、香港理工大学、马里兰大学、华盛顿大学圣路易斯分校、UIUC、新加坡管理大学等多机构联合发布了一篇关于 Self-Evolving Agents(自进化智能体) 的系统性综述: A Systematic Survey of Self-Evolving Agents: From M…...

告别复杂抠图!ComfyUI-BiRefNet-ZHO:5分钟实现专业级图像视频背景去除

告别复杂抠图!ComfyUI-BiRefNet-ZHO:5分钟实现专业级图像视频背景去除 【免费下载链接】ComfyUI-BiRefNet-ZHO Better version for BiRefNet in ComfyUI | Both img & video 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-BiRefNet-ZHO …...

3步解锁Unity游戏无限可能:MelonLoader模组加载器完全指南

3步解锁Unity游戏无限可能:MelonLoader模组加载器完全指南 【免费下载链接】MelonLoader The Worlds First Universal Mod Loader for Unity Games compatible with both Il2Cpp and Mono 项目地址: https://gitcode.com/gh_mirrors/me/MelonLoader 你是否曾…...

从Windows桌面到Raspberry Pi Zero W2:.NET 9跨架构边缘调试7大约束条件对照表,第4项已被微软标记为P0阻塞问题

更多请点击: https://intelliparadigm.com 第一章:.NET 9跨架构边缘调试的演进背景与核心挑战 随着物联网与边缘计算场景爆发式增长,.NET 应用正加速部署于 ARM64、RISC-V 等异构硬件平台。.NET 9 首次将跨架构调试能力深度集成至 dotnet-du…...

【紧急预警】DOTS 2.0正式版中已被移除的API兼容层正在 silently 拖垮你的构建速度:3类高危Deprecated调用检测脚本(附自动化修复工具)

更多请点击: https://intelliparadigm.com 第一章:DOTS 2.0构建性能退化根源的紧急定位与认知升级 在 Unity DOTS 2.0 生态中,构建(Build)阶段的性能退化往往隐匿于 JobSystem 调度器初始化、Burst 编译缓存失效或 En…...

HiveWE完整指南:现代化地图编辑器让魔兽争霸3地图制作变得简单

HiveWE完整指南:现代化地图编辑器让魔兽争霸3地图制作变得简单 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 还在为传统魔兽争霸3地图编辑器的卡顿和复杂操作而烦恼吗?HiveWE是一款…...

12306ForMac:macOS原生抢票助手的深度开发指南

12306ForMac:macOS原生抢票助手的深度开发指南 【免费下载链接】12306ForMac An unofficial 12306 Client for Mac 项目地址: https://gitcode.com/gh_mirrors/12/12306ForMac 还在为节假日抢票而烦恼吗?作为Mac用户,你是否厌倦了在虚…...