【从零开始学数据结构①】:顺序表

【从零开始学数据结构①】:顺序表

学完c语言的基础语法,我们便要开始数据结构的学习,那么什么是数据结构呢,听我给你一一道来吧!

一.数据结构

1.什么是数据结构?

定义:数据结构是计算机存储、组织数据的方式。

2.为什么要使用数据结构?

在前面几章中我们学习到了数组这一概念,用下标来管理数据,但是你有没有想过,如果数据量过大,仅用数组便于管理和使用吗?

显然有些吃力了,那么这里便是为什么使用数据结构的原因了,以结构的形式进行数据管理。

二.顺序表

1.线性表

在了解顺序表前先让大家了解一下线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中广泛使⽤的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

2.顺序表的定义

顺序表是一种线性表的顺序存储结构,底层结构为数组,即以一组地址连续的存储单元进行存储。

顺序表中可以存储任何类型的数据(整型,字符型,结构体,指针…)

3.顺序表的分类

(1)静态顺序表

静态顺序表,顾名思义大小是静态不可改变的,采用定长数组进行创建。

//静态顺序表typedefint SLDataType;//将数据类型更名,方便以后的数据类型变化//例如:后需要存储char类型,这样就可以全部更改#defineN10//宏定义N的值为10,方便后续使用,只需改一处typedefstructSeqList//给机构体重新命名方便后续使用{ SLDataType arr[N];//定长数组int size;//有效数据个数}SL;

静态顺序表的缺点:空间确定,给多了浪费,给少了不够。

(2)动态顺序表

与静态顺序表不同的则是拥有了动态改变内存大小的功能,运用malloc, realloc按需求进行申请内存或改变内存大小。

//动态顺序表typedefint SLDataType;typedefstructSeqList{ SLDataType* arr;//因为动态申请内存,所以有首元素地址即可int size;//有效数据个数(可以访问的元素个数)int capacity;//空间大小}SL;

三.动态顺序表的功能实现

因为顺序表是为了管理数据,所以应该有增删查找等功能,接下来我将从这几点来实现功能。

注:创建顺序表示,应该有三个文件

  • 顺序表的头文件(SeqList.h)
  • 顺序表的功能文件(SeqList.c)

顺序表的测试文件(test.c)

在这里插入图片描述


创建好以上文件我们就可以开始创建顺序表了,准备好开始上高速!

1.顺序表的定义

//SeqList.h#include<stdio.h>#include<stdlib.h>#include<assert.h>//把要是用的头文件都包含在SeqList.h中typedefint SLDataType;typedefstructSeqList{ SLDataType* arr;int size;int capacity;}SL;

2.顺序表的初始化

像创建其他变量一样,在定义完后我们需要对其进行初始化

在这里插入图片描述


在这里插入图片描述

可以在测试文件测试功能是否成功运行,注意每实现一个功能就进行一次测试,切记不要写了多个功能后一起测试,这样会使得我们不易找到错误!

在这里插入图片描述


在这里插入图片描述

先在头文件中声明后在功能文件实现完整功能。
注意:因为初始化需要改变实参所以要采用传递指针,而非简单传递值。

3.顺序表的销毁

为什么在这里先讲销毁呢?因为在顺序表中的内存是由动态申请来的,所以需要销毁申请的堆区内存

在这里插入图片描述


在这里插入图片描述

在这里需要注意!内存在被回收后不会自动置为空指针,若不手动置空,则成了野指针!!有非法访问的可能。

4.顺序表数据的增添

那么接下来我们就要依次学习顺序表的增删查找功能了!

数据的增添又分为两种

  • 头插(即在最开始添加数据)
  • 尾插(即在末尾添加数据)
  • 指定位置插入

在这里我们又易到难来展开讲解

a.尾插

在顺序表的末尾插入数据
核心思路:找到末尾下标,在此下标处插入数据
在插入数据前我们需要先检查空间是否足够,若不够则需要扩容

//检查空间是否充足voidSLCheck(SL *sp){assert(sp);//确保参数不为空指针if(sp->size == sp->capacity)//当有效个数与空间容量相等时则证明空间不够//此情况包含了size和capccity为0时的情况,十分巧妙{//空间不够时,按倍数进行扩增int newcapcity = capacity ==0?4: sp->capacity *2; SLDataType* tmp =(SLDataType*)realloc(sp->arr, newcapacity *sizeof(SLDataType));//检查是否扩容成功if(tmp ==NULL){perror("realloc");exit(1);}//由我们所定义的标识符所维护 sp->capacity = newcapacity; sp->arr = tmp;}}

空间充足时开始插入数据:

//尾插voidSLPushBack(SL* sp, SLDataType x){assert(sp);//检查空间SLCheck(sl);//传值即可,因为我们只需判断无需改变 sp->arr[sp->size]= x; sp->size++;//增添一个数据后size自增}

在test文件中我们可以检查一下尾插是否正确

在这里插入图片描述


在这里插入图片描述


在这里我们可以看到,申请的四个空间中已经插入了两个数据,但是我们发现这样检查不是很方便所以我们可以写一个打印函数来将顺序表打印出来。

//打印顺序表voidSLPrint(SL* sl){for(int i =0; i < sl->size; i++){printf("%d ", sl->arr[i]);}}
zheyang


这样一来就可以直观的方便的看到数据的增添与删减变化,无需通过调试来检查。

b.头插

在顺序表的头部插入数据

核心思路:整体元素向后移动,然后将数据添加在首元素位置。

与尾插相似,在插入数据时我们要先检查空间是否足够,若空间不足,则整体移动时可能造成越界非法访问内存

在这里插入图片描述


接下来我们来写头插部分

思考一下
要整体移动数据是应该从前往后覆盖还是从后往前覆盖呢?

很容易得出答案,那边是从后往前,如果从前往后的话,后一个数据会被前一个数据所覆盖而导致数据改变。

在这里插入图片描述

此时下标为1的位置如果要往后移动,则移动数值为1,而不是2,因为从前往后覆盖数值被改变。

从后往前:

在这里插入图片描述

这样就可以很好的避免数据丢失,若你学过内存函数,你是否想起一个函数叫memmove?用此函数也可以解决问题,比起for循环更有效率 。

那么下面我们就分别用for循环和memmove函数来进行练习。

for循环版本

在这里插入图片描述

注意在写for循环时,我们的条件和调整部分可能还不知道,我们可以先写成伪代码,即没有条件只有结构的代码,将循环体的内容先写出,在得出循环条件以及调整条件。

memmove版本:

在这里插入图片描述

测试一下

在这里插入图片描述
在这里插入图片描述
c.指定位置插入

核心思路: 找到指定位置,并插入数据

同理:在找到指定位置后,需要将此元素后的所有元素都向后移动一位,故也有两种移动方法,for循环法和memmove法。

//指定位置插入数据(for循环版)voidSLPushInsert(SL* sl,int pos, SLDataType x){assert(sl);//判断传入指针是否为空//判断要插入位置是否合法assert(pos >=0&& pos <= sl->size);SLCheck(sl);//判断空间是否足够for(int i = sl->size; i >0; i--){ sl->arr[i]= sl->arr[i -1];} sl->arr[pos]= x;//插入数据 sl->size++;//别忘记有效数据个数加一 }
//指定位置插入数据(memmove版)voidSLPushInsert(SL* sl,int pos, SLDataType x){assert(sl);assert(pos >=0&& pos <= sl->size);SLCheck(sl);memmove(sl->arr +1; sl->arr;sizeof(SLDataType)* sl->size); sl->arr[pos]= x; sl->size++;}

测试一下:

在这里插入图片描述


在这里插入图片描述

到这里了顺序表数据的增添就完成啦。

那么有了添加,接下来我们就来讲讲删除。

5.顺序表数据的删除

与插入数据一样,删除数据也有几种

  • 头删(删除第一个数据)
  • 尾删(删除末尾的数据)
  • 指定位置删除(删除指定位置元素)

像增添数据一样,我们也将从易到难展开讲解

a.尾删

核心思想:抹除最后一个数据

看到这里你可能会想将数据直接删除,而我们前面讲到顺序表中有效数据个数是我们能访问到的元素,若把有效数据个数减一,则之前的数据无法被访问,即被删除。

下面看我演示:

voidSLPopBack(SL* sl){ sl->size--;}
在这里插入图片描述


在这里插入图片描述

尾删是不是很简单?那么我们再来看头删

b.头删

核心思想:首元素后面的元素一次往前移动一个位置,往前覆盖。

思考:移动时应从前往后,而非从后往前,防止数据丢失。

//头删voidSLPopFront(SL* sl){assert(sl);for(int i =0; i < sl->size; i++){ sl->arr[i]= sl->arr[i +1];} sl->size--;}
、


在这里插入图片描述

头尾这类我们解决了那么再来看指定位置删除

c.指定位置删除

核心思想:指定位置处后的元素依次往前覆盖

//指定位置删除voidSLPopErase(SL* sl,int pos){assert(sl);assert(pos >=0&& pos <= sl->size);for(int i = pos; i < sl->size; i++){ sl->arr[i]= sl->arr[i +1];} sl->size--;}
在这里插入图片描述


在这里插入图片描述


到这里我们顺序表删除的功能就结束了!

接下来是查找信息

6.顺序表数据的查找

前面说到顺序表是以结构的形式进行数据管理,那么数据量大时,数据查找是必不可少的一个功能,那么我们开始编写数据的查找功能。

在我们写的顺序表中数据为整型,所以我们可以通过输入整型进行查询数据,大家在以后的学习中,数据类型可以为结构体等类型,所以要查找的数据也有很多种,在这里我们只用整型来演示。

核心思想:通过遍历来找寻给定数据

//顺序表的查询voidSLFind(SL* sl, SLDataType x){assert(sl);for(int i =0; i < sl->size; i++){if(sl->arr[i]== x){printf("查找到数据,下标为:%d", i);return;}}printf("未查找到数据\n");}
在这里插入图片描述


在这里插入图片描述

7.顺序表数据的修改

核心思想:找到数据覆盖即可

//顺序表的修改voidSLModify(SL* sl, SLDataType x, SLDataType y){assert(sl);for(int i =0; i < sl->size; i++){if(sl->arr[i]== x){ sl->arr[i]= y;}}}
在这里插入图片描述


在这里插入图片描述

到这里顺序表的定义和实现就都完成了,通过学习顺序表我们可以完成一个小项目,那便是通讯录。

通讯录的功能便是增删查改,只是将顺序表的数组改成了结构体,在结构体内存放姓名,联系方式方式等内容。

【从零开始学数据结构①】: 顺序表部分结束,我们下期再见!

Read more

在 Cursor 中打造你的专属前端“AI 助手”:Agent Skills 实战指南 什么是 Agent Skills?

在 Cursor 中打造你的专属前端“AI 助手”:Agent Skills 实战指南 什么是 Agent Skills?

文章目录 * 一、什么是 Agent Skills? * 二、使用步骤 * 1.下载官方提供的agent-skills文档 * 2.cursor中使用 * 三、如何设计自己的skills * 四、实战:打造一个“生成标准 React 组件”的 Skill * 第一步:创建目录 * 第二步:编写 SKILL.md * 总结:为什么你应该开始用 Skills? 一、什么是 Agent Skills? 简单来说,Agent Skills 是一种标准化的方式,用来封装特定任务的知识和工作流。 如果说 MCP (Model Context Protocol) 是给 AI 装上了“手”(让它能连接数据库、Github)

By Ne0inhk

DeepSeek-OCR本地部署实战|基于DeepSeek-OCR-WEBUI镜像快速搭建

DeepSeek-OCR本地部署实战|基于DeepSeek-OCR-WEBUI镜像快速搭建 1. 引言 1.1 OCR技术的演进与挑战 光学字符识别(OCR)作为连接图像与文本信息的关键技术,已广泛应用于文档数字化、票据处理、身份验证等场景。随着深度学习的发展,传统OCR系统在复杂背景、低分辨率、手写体等场景下的局限性逐渐显现。近年来,大模型驱动的OCR系统凭借更强的泛化能力和上下文理解能力,显著提升了识别准确率和鲁棒性。 DeepSeek-OCR正是这一趋势下的代表性成果。它不仅具备高精度的文本检测与识别能力,还融合了先进的注意力机制和后处理优化模块,在中文场景下表现尤为突出。然而,其庞大的模型规模也带来了部署门槛高的问题——依赖复杂的环境配置、显存需求大、推理延迟高等。 1.2 部署痛点与解决方案 传统的手动部署方式需要依次完成以下步骤: - 创建虚拟环境 - 安装PyTorch及CUDA兼容版本 - 克隆项目代码并安装数十项依赖 - 下载多GB级别的模型文件 - 调整配置参数以适配本地硬件 这一过程耗时长、容错率低,尤其对新手极不友好。为解决该问题,DeepSee

By Ne0inhk
网站检测不用等! Web-Check+cpolar让异地协作查漏洞更高效

网站检测不用等! Web-Check+cpolar让异地协作查漏洞更高效

文章目录 * 前言 * 1.关于Web-Check * 2.功能特点 * 3.安装Docker * 4.创建并启动Web-Check容器 * 5.本地访问测试 * 6.公网远程访问本地Web-Check * 7.内网穿透工具安装 * 8.创建远程连接公网地址 * 9.使用固定公网地址远程访问 前言 Web-Check 是一款全方位的网站诊断工具,能检测 IP 信息、SSL 证书、DNS 记录、开放端口等关键数据,适合开发者做性能优化、运维人员做安全巡检,还能帮安全测试人员识别潜在风险。它的优点是结果可视化强,所有数据在仪表盘分类呈现,不用手动整合多工具报告,省时又清晰。 用 Web-Check 时发现,检测前最好确认目标网站能正常访问,否则可能出现数据不全;另外,生成的报告里有不少专业术语,新手可以先查基础概念(比如 SSL 链、DNS

By Ne0inhk
基于C++11手撸前端Promise

基于C++11手撸前端Promise

文章导航 * 引言 * 前端Promise的应用与优势 * 常见应用场景 * 并发请求 * Promise 解决的问题 * 手写 C++ Promise 实现 * 类结构与成员变量 * 构造函数 * resolve 方法 * reject 方法 * then 方法 * onCatch 方法 * 链式调用 * 使用示例 * `std::promise` 与 `CProimse` 对比 * 1. 基础功能对比 * 2. 实现细节对比 * (1) 状态管理 * (2) 回调注册与执行 * (3) 异步支持 * (4) 链式调用 * 3. 代码示例对比 * (1) `CProimse` 示例 * (2) `std::promise` 示例 * 4.

By Ne0inhk