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

数据结构初阶: 顺序表的增删查改

顺序表

概念

顺序表是⽤⼀段物理地址连续的存储单元依次存储数据元素的线性结构,⼀般情况下采⽤数组存储。如图1:

顺序表和数组有什么区别?

顺序表的底层是用数组实现的,是对数组的封装,实现了增删查改等接口。

分类

静态顺序表

概念:使用定长数组存储数据

缺陷:空间给少了不够⽤,给多了造成空间浪费

动态顺序表

动态顺序表顾名思义长度是可以灵活改变的,按需申请空间

动态顺序表的实现

先明确顺序表所需的函数

// 顺序表初始化
void SeqListInit(SeqList* ps);
// 检查顺序表是否满了,满了需要扩容
void SLCheckCapacity(SeqList* ps)
// 顺序表的销毁
void SeqListDestroy(SeqList* ps);
// 顺序表的输出
void SeqListPrint(SeqList* ps);
// 顺序表尾插
void SeqListPushBack(SeqList* ps, SLDateType x);
// 顺序表头插
void SeqListPushFront(SeqList* ps, SLDateType x);
// 顺序表头删
void SeqListPopFront(SeqList* ps);
// 顺序表尾删
void SeqListPopBack(SeqList* ps);
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在任意位置插入数据
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除任意位置的值
void SeqListErase(SeqList* ps, int pos);

顺序表的初始化

顺序表需要先进行初始化,如果不初始化,内存内容不确定

  • 顺序表中的元素会包含内存中的随机值(垃圾值)

  • 这些值是不可预测的,可能导致程序行为不一致

还会有安全隐患

  • 未初始化的内存可能包含敏感信息(如果之前存储过)

  • 可能被恶意利用(如信息泄露)

程序错误

  1. 逻辑错误:使用未初始化的值进行计算会导致错误结果

  2. 崩溃风险:如果值被当作指针使用可能导致段错误

  3. 不可重现的bug:在不同环境/时间运行可能表现不同

性能影响

  • 某些情况下,初始化内存可以优化后续访问性能

  • 现代CPU对初始化的内存访问可能有更好的预取行为

// 顺序表数组初始化
void SeqListInit(SeqList* ps)
{//ps->arr = NULL;//ps->size = ps->capacity = 0;ps->arr = (SLDateType*)malloc(sizeof(SLDateType) * 4);if (ps->arr == NULL){printf("空间开辟失败\n");exit(-1);}ps->size = 0;ps->capacity = 4;
}

检查顺序表空间是否需要扩容

当有效数据大于空间时需要对数组进行扩容,扩容通常进行2倍扩容

1. 均摊时间复杂度优化

  • 均摊O(1)插入:2倍扩容可以保证在多次插入操作中,扩容的均摊时间复杂度为O(1)

  • 数学证明:经过n次插入后,总复制次数约为n + n/2 + n/4 + ... ≈ 2n

2. 内存使用效率

  • 空间浪费与复制的平衡:2倍扩容在空间浪费和复制次数之间取得了较好平衡

  • 过小扩容因子(如1.1倍)会导致频繁复制

  • 过大扩容因子(如3倍)会导致过多内存浪费

3. 内存分配器的特性

  • 许多内存分配器对2的幂次大小有优化

  • 2倍扩容减少内存碎片,便于内存池管理

4. 实际实现中的变体

  • 不同语言/库可能有不同选择:

    • Java ArrayList:初始容量10,扩容1.5倍

    • Python list:初始空或根据元素,扩容约1.125倍

    • C++ vector:标准未规定,通常实现为2倍

5. 为什么不是更大的倍数?

  • 内存浪费问题:3倍或更大倍数会导致显著的内存空间闲置

  • 局部性原理:过大的扩容可能使数据分散在不同内存页

数学视角

假设每次扩容因子为k:

  • 扩容次数为O(logₖn)

  • 总复制量为n(1 + 1/k + 1/k² + ...) = n×k/(k-1)

  • k=2时,总复制量≈2n

  • k=1.5时,总复制量≈3n

因此2倍是在复制次数和内存利用率之间的一个合理折中。

void SLCheckCapacity(SeqList* ps)
{// 判断数组空间是否满了if (ps->size >= ps->capacity){ps->capacity *= 2;SLDateType* tmp = (SLDateType*)realloc(ps->arr, sizeof(SLDateType) * ps->capacity);if (tmp == NULL){printf("空间开辟失败\n");exit(-1);}ps->arr = tmp;}
}

顺序表的销毁

1. 内存资源释放

  • 防止内存泄漏:动态分配的内存必须显式释放,否则会随着程序运行不断累积

  • 系统资源回收:将不再使用的内存归还给操作系统,供其他程序使用

  • 长期运行程序的关键:对于服务器、后台服务等长期运行程序尤为重要

2. 数据结构生命周期管理

  • 明确所有权:销毁操作明确了谁负责释放资源(创建者负责销毁)

  • 状态重置:通过销毁操作将数据结构标记为"无效状态",防止后续误用

3. 程序健壮性保障

  • 避免悬垂指针:规范的销毁操作会将被释放指针置NULL(如ps->arr = NULL

  • 防御性编程:良好的销毁函数应包含NULL指针检查等安全措施

4. 系统稳定性

  • 防止内存碎片:及时释放不再使用的内存块有助于减少内存碎片

  • 资源竞争预防:在多线程环境中,明确的销毁操作可以避免资源竞争

void SeqListDestroy(SeqList* ps)
{assert(ps);  // 检查空指针if (ps->arr)    // 先释放内部数组free(ps->arr);ps->arr = NULL;  // 避免悬垂指针ps->capacity = ps->size = 0;
}

顺序表的输出

便于观察顺序标中的数据

void SeqListPrint(SeqList* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
}

顺序表的插入

尾插

void SeqListPushBack(SeqList* ps, SLDateType x)
{assert(ps);            // 确保ps不为空指针SLCheckCapacity(ps);   // 检查空间是否需要扩容ps->arr[ps->size] = x; // 将x直接插入最后一个位置ps->size++;            // 而后size自增
}

 头插

void SeqListPushFront(SeqList* ps, SLDateType x)
{assert(ps);SLCheckCapacity(ps);for (int i = ps->size; i > 0 ; i--){// 将数组从最后一位开始往后移ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;++ps->size;
}

 任意位置插入

void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{assert(ps);assert(pos <= ps->size && pos >= 0);SLCheckCapacity(ps);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;++ps->size;
}

优化头插尾插 

void SeqListPushBack(SeqList* ps, SLDateType x)
{SeqListInsert(ps, ps->size, x);
}void SeqListPushFront(SeqList* ps, SLDateType x)
{SeqListInsert(ps, 0, x);
}

顺序表的删除

尾删

void SeqListPopBack(SeqList* ps)
{assert(ps && ps->size);--ps->size;
}

 头删

void SeqListPopFront(SeqList* ps)
{assert(ps && ps->size);for (int i = 1; i < ps->size; i++){ps->arr[i - 1] = ps->arr[i];}--ps->size;
}

任意位置删除

void SeqListErase(SeqList* ps, int pos)
{assert(ps);assert(pos >= 0 && pos <= ps->size);int start = pos;while (start < ps->size){ps->arr[start] = ps->arr[start + 1];start++;}--ps->size;
}

优化尾删头删

void SeqListPopBack(SeqList* ps)
{SeqListErase(ps, ps->size);
}void SeqListPopFront(SeqList* ps)
{SeqListErase(ps, 0);
}

顺序表查找

int SeqListFind(SeqList* ps, SLDateType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i; // 找到数据返回下标}}return -1; // 如果没有数据返回-1
}

相关文章:

数据结构初阶: 顺序表的增删查改

顺序表 概念 顺序表是⽤⼀段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;⼀般情况下采⽤数组存储。如图1&#xff1a; 顺序表和数组有什么区别&#xff1f; 顺序表的底层是用数组实现的&#xff0c;是对数组的封装&#xff0c;实现了增删查改等接口。 分…...

Spring Boot项目中策略模式的应用与实现

前言 在Spring Boot项目中&#xff0c;策略模式是一种非常重要的设计模式&#xff0c;它能够让我们定义一系列算法&#xff0c;并使它们可以互相替换。 策略模式通过将算法封装到独立的类中&#xff0c;从而使得代码中的算法可以独立于使用它的客户端变化。 这对于某些需求频…...

【机器学习中的基本术语:特征、样本、训练集、测试集、监督/无监督学习】

机器学习基本术语详解 1. 特征&#xff08;Feature&#xff09; 定义&#xff1a;数据的属性或变量&#xff0c;用于描述样本的某个方面。作用&#xff1a;模型通过学习特征与目标之间的关系进行预测。示例&#xff1a; 预测房价时&#xff0c;特征可以是 面积、地段、房龄。…...

MySQL全链路指南

目录 前言 第一章 MySQL基础入门 1.1 MySQL简介与安装 1.2 数据库基本操作 1.3 表结构与数据类型 第二章 SQL语言深度解析 2.1 DDL&#xff08;数据定义语言&#xff09; 2.2 DML&#xff08;数据操作语言&#xff09; 2.3 DQL&#xff08;数据查询语言&#xff09; 2…...

System.arraycopy()

在 Java 编程中&#xff0c;数组是一种常用的数据结构&#xff0c;用于存储相同类型的元素集合。在处理数组时&#xff0c;经常需要进行数组复制操作&#xff0c;例如将一个数组的部分或全部元素复制到另一个数组中。System.arraycopy() 方法是 Java 提供的一个高效的数组复制工…...

详解AI采集框架Crawl4AI,打造智能网络爬虫

大家好&#xff0c;Crawl4AI作为开源Python库&#xff0c;专门用来简化网页爬取和数据提取的工作。它不仅功能强大、灵活&#xff0c;而且全异步的设计让处理速度更快&#xff0c;稳定性更好。无论是构建AI项目还是提升语言模型的性能&#xff0c;Crawl4AI都能帮您简化工作流程…...

【爬虫开发】爬虫开发从0到1全知识教程第14篇:scrapy爬虫框架,介绍【附代码文档】

本教程的知识点为&#xff1a;爬虫概要 爬虫基础 爬虫概述 知识点&#xff1a; 1. 爬虫的概念 requests模块 requests模块 知识点&#xff1a; 1. requests模块介绍 1.1 requests模块的作用&#xff1a; 数据提取概要 数据提取概述 知识点 1. 响应内容的分类 知识点&#xff1a…...

MySQL索引原理:从B+树手绘到EXPLAIN

最近在学后端&#xff0c;学到了这里做个记录 一、为什么索引像书的目录&#xff1f; 类比&#xff1a;500页的技术书籍 vs 10页的目录缺点&#xff1a;全表扫描就像逐页翻找内容优点&#xff1a;索引将查询速度从O(n)提升到O(log n) 二、B树手绘课堂 1. 结构解剖&#xff0…...

SQLark:一款国产免费数据库开发和管理工具

SQLark&#xff08;百灵连接&#xff09;是一款面向信创应用开发者的数据库开发和管理工具&#xff0c;用于快速查询、创建和管理不同类型的数据库系统&#xff0c;目前可以支持达梦数据库、Oracle 以及 MySQL。 对象管理 SQLark 支持丰富的数据库对象管理功能&#xff0c;包括…...

防爆对讲机VS非防爆对讲机,如何选择?

在通信设备的广阔市场中&#xff0c;对讲机以其高效、便捷的特点&#xff0c;成为众多行业不可或缺的沟通工具。而面对防爆对讲机与非防爆对讲机&#xff0c;许多用户常常陷入选择困境。究竟该如何抉择&#xff0c;且听我为您细细道来。 防爆对讲机&#xff0c;专为危险作业场…...

微信小程序开发:开发实践

微信小程序开发实践研究 摘要 随着移动互联网的迅猛发展&#xff0c;微信小程序作为一种轻量化、无需安装的应用形式&#xff0c;逐渐成为开发者和用户的首选。本文以“个人名片”小程序为例&#xff0c;详细阐述了微信小程序的开发流程&#xff0c;包括需求分析、项目规划、…...

操作 Office Excel 文档类库Excelize

Excelize 是 Go 语言编写的一个用来操作 Office Excel 文档类库&#xff0c;基于 ECMA-376 OOXML 技术标准。可以使用它来读取、写入 XLSX 文件&#xff0c;相比较其他的开源类库&#xff0c;Excelize 支持操作带有数据透视表、切片器、图表与图片的 Excel 并支持向 Excel 中插…...

青铜与信隼的史诗——TCP与UDP的千年博弈

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 第一章 契约之匣与自由之羽 熔岩尚未冷却的铸造台上&#xff0c;初代信使长欧诺弥亚将液态秘银倒入双生模具。左侧模具刻着交握的青铜手掌&#xff0c;右侧则是展开的隼翼纹章。当星辰…...

「青牛科技」GC5849 12V三相无感正弦波电机驱动芯片

芯片描述&#xff1a; • 4 &#xff5e; 20V 工作电压&#xff0c; 30V 最大耐压 • 驱动峰值电流 2.0A &#xff0c;连续电流 800mA 以内 • 芯片内阻&#xff1a; 900mΩ &#xff08;上桥 下桥&#xff09; • eSOP-8 封装&#xff0c;底部 ePAD 散热&#xff0c;引…...

Java基础之反射的基本使用

简介 在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意属性和方法&#xff1b;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。反射让Java成为了一门动…...

大语言模型中的嵌入模型

本教程将拆解什么是嵌入模型、为什么它们在NLP中如此重要,并提供一个简单的Python实战示例。 分词器将原始文本转换为token和ID,而嵌入模型则将这些ID映射为密集向量表示。二者合力为LLMs的语义理解提供动力。图片来源:[https://tzamtzis.gr/2024/coding/tokenization-by-an…...

【从零实现Json-Rpc框架】- 项目实现 - 服务端主题实现及整体封装

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…...

位置编码(Positional Encoding, PE)的作用

在神经网络&#xff08;尤其是Transformer、RNN等序列模型&#xff09;中&#xff0c;位置编码&#xff08;Positional Encoding, PE&#xff09;的作用是为模型提供序列中元素的位置信息&#xff0c;以弥补模型本身对顺序感知的不足。 为什么Transformer需要位置编码&#xf…...

开源的 LLM 应用开发平台Dify的安装和使用

文章目录 前提环境应用安装deocker desktop镜像源配置Dify简介Dify本地docker安装Dify安装ollama插件Dify安装硅基流动插件简单应用练习进阶应用练习数据库图像检索与展示助手echart助手可视化 前提环境 Windows环境 docker desktop魔法环境&#xff1a;访问Dify项目ollama电脑…...

从零构建大语言模型全栈开发指南:第五部分:行业应用与前沿探索-5.1.2行业落地挑战:算力成本与数据隐私解决方案

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 从零构建大语言模型全栈开发指南-第五部分:行业应用与前沿探索5.1.2 行业落地挑战:算力成本与数据隐私解决方案1. 算力成本挑战与优化策略1.1 算力成本的核心问题1.2 算力优化技术方案2. 数据隐私挑战…...

NodeJS--NPM介绍使用

1、使用npm install命令安装模块 1.1、本地安装 npm install express 1.2、全局安装 npm install express -g 1.3、本地安装和全局安装的区别...

DeepSeek与ChatGPT的优势对比:选择合适的工具来提升工作效率

选DeepSeek还是ChatGPT&#xff1f;这就像问火锅和披萨哪个香&#xff01; "到底该用DeepSeek还是ChatGPT?” 这个问题最近在互联网圈吵翻天!其实这就跟选手机系统-样&#xff0c;安卓党iOS党都能说出一万条理由&#xff0c;但真正重要的是你拿它来干啥&#xff01;&am…...

lib-zo,C语言另一个协程库,sleep协程化,睡眠

lib-zo,C语言另一个协程库,sleep协程化,睡眠 另一个 C 协程库 https://blog.csdn.net/eli960/article/details/146802313 重载了 sleep 函数, 使其支持协程化 另外毫秒单位睡眠函数 void zcoroutine_sleep_millisecond(int milliseconds);例子 #include "coroutine.h…...

25大唐杯赛道一本科B组知识点大纲(下)

5G/6G网络技术知识点&#xff08;10%&#xff09; 工程概论及通信工程项目实践&#xff08;20%&#xff09; 5G垂直行业应用知识点&#xff08;20%&#xff09; ⭐⭐⭐为重点知识&#xff0c;尽量要过一遍哦 大唐杯赛道一国一备赛思路 大唐杯国一省赛回忆录--有付出就会有收…...

Python+Playwright自动化测试-1-环境准备与搭建

1、Playwright 是什么&#xff1f; 微软在 2020 年初开源的新一代自动化测试工具&#xff0c;它的功能类似于 Selenium、Pyppeteer 等&#xff0c;都可以驱动浏览器进行各种自动化操作。它的功能也非常强大&#xff0c;对市面上的主流浏览器都提供了支持&#xff0c;API 功能简…...

生产管理系统如何破解汽车零部件行业追溯难痛点

在汽车零部件制造行业中&#xff0c;生产追溯一直是企业面临的核心挑战之一。随着市场竞争的加剧和客户需求的日益复杂&#xff0c;如何确保产品质量、快速定位问题源头、减少批次性返工&#xff0c;成为了每个企业亟待解决的问题。而生产管理系统&#xff0c;作为智能制造的重…...

【XTerminal】【树莓派】Linux系统下的函数调用编程

目录 一、XTerminal下的Linux系统调用编程 1.1理解进程和线程的概念并在Linux系统下完成相应操作 (1) 进程 (2)线程 (3) 进程 vs 线程 (4)Linux 下的实践操作 1.2Linux的“虚拟内存管理”和stm32正式物理内存&#xff08;内存映射&#xff09;的区别 (1)Linux虚拟内存管…...

umi框架开发移动端h5

1、官网&#xff1a;https://umijs.org/ 2、创建出来的项目 yarn create umi yarn start3、推荐目录结构 . ├── config │ └── config.ts ├── public//静态资源 ├── dist ├── mock │ └── app.ts&#xff5c;tsx ├── src │ ├── .umi │ ├── .um…...

TDengine 重磅功能虚拟表

简介 虚拟表功能是 TDengine 最近刚发现的 3.3.6.0 版本中一项重磅级新功能&#xff0c;虚拟表可理解为在原来查询基础上做了一层逻辑表&#xff0c;在数据查询建模时即可不依赖底层物理存储表&#xff0c;直接通过虚拟表进行数据查询建模&#xff0c;这样逻辑上会更加清晰&am…...

3.9/Q2,Charls最新文章解读

文章题目&#xff1a;Association between remnant cholesterol and depression in middle-aged and older Chinese adults: a population-based cohort study DOI&#xff1a;10.3389/fendo.2025.1456370 中文标题&#xff1a;中国中老年人残留胆固醇与抑郁症的关系&#xff1…...