C语言—顺序表(含通讯录项目)

目录

一、顺序表的概念及结构

二、顺序表的分类

(2.1)静态顺序表

(2.2)动态顺序表

三、动态顺序表的实现

(3.1)基本结构定义

(3.2)初始化和销毁

(3.2.1)初始化—SLInit

(3.2.2)销毁—SLDestroy

(3.2.3)打印函数—SLPrint

(3.3)检查扩容

(3.4)头部插入删除\尾部插入删除

(3.4.1)尾部插入

(3.4.2)尾部删除

(3.4.3)头部插入

(3.4.4)头部删除

(3.5)指定位置插入\删除数据

(3.5.1)指定位置插入数据

(3.5.2)指定位置删除数据

(3.6)查找指定数据

四、通讯录项目

(4.1)通讯录功能

(4.2)动态顺序表的改变

(4.2.1)存储数据类型的改变

(4.2.2)打印函数的改变

(4.2.3)查找函数的改变

(4.2.4)添加SLSize函数

(4.2.5)添加SLAt函数

(4.3)通讯录基本结构定义

(4.4)通讯录项目实现的函数

(4.4.1)初始化通讯录

(4.4.2)添加通讯录数据

(4.4.3)删除通讯录数据

(4.4.4)展示通讯录数据

(4.4.5)查找通讯录数据

(4.4.6)修改通讯录数据

(4.4.7)销毁通讯录数据

(4.5)测试


一、顺序表的概念及结构

要想了解顺序表,首先我们要知道线性表,因为顺序表也是线性表的一种。

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

二、顺序表的分类

(2.1)静态顺序表

概念:使用定长数组存储元素。

基本定义格式如下:

静态顺序表缺点:空间给少了不够用,给多了造成空间浪费。

(2.2)动态顺序表

动态顺序表底层仍是数组,但不像静态顺序表那样给出指定大小的数组,而是给一个需要存储的数据类型的指针,使用时通过动态开辟内存,并让指针指向动态开辟的空间,前面文章说过,动态开辟的内存是可以通过realloc函数进行扩容的,这样就达到了按需申请的效果,避免了空间不够用,也一定程度上缓解了空间的浪费。

基本定义格式如下:

注意:顺序表和数组的区别在于顺序表的底层结构是数组,对数组进行封装,实现了常用的增删改查等接口。

三、动态顺序表的实现

(3.1)基本结构定义

这里一共创建了三个文件,SeqList.h是用来放头文件,结构体定义,函数声明的;SeqList.c是用来对声明的函数进行定义的;test.c是对写好的动态顺序表进行测试的。

第一部分是在整个顺序表程序中需要用到的头文件(assert.h是为了使用断言 - assert(),assert函数中可以放表达式,若表达式为假,则终止程序并给出错误提示信息,如果想让程序中的断言失效可以定义宏#define NDEBUG)。

第二部分是为了让程序更具有通用性,如果写死指针 a 指向的数据类型,那么当前顺序表就只能应用于这种数据类型,如果想应用于其他数据类型,只能重新写一个顺序表或者对当前顺序表进行彻底的更改,而对当前需要存储的数据类型进行重命名,后面接口都是使用重命名后的名字,这样应用于其他数据类型是只需要将 2 语句中的 int 改成想存储的数据类型就可以了。

第三部分是对动态顺序表结构的定义,一个指向存储数据空间的指针—a,一个记录数组中的有效元素的个数—size,一个记录整个数组的大小(即最多可以存储多少个数据)—capacity。

(3.2)初始化和销毁

(3.2.1)初始化—SLInit

如上图所示,动态顺序表的底层是动态开辟出来的数组,因为这里我们实现的动态顺序表是在第一个数据插入时才为其中的数组开辟空间,而初始化又是对刚创建好的顺序表中的数据进行一个使用前的设置,所以在初始化函数中我们可以先将指针置空,size和capacity置0。

函数使用场景:

在创建好动态顺序表后直接进行初始化。(前面将struct SeqList重命名为了SL)

测试:

(3.2.2)销毁—SLDestroy

使用完动态顺序表后我们需要主动对动态顺序表进行销毁,因为动态顺序表中数组空间是利用动态开辟内存函数在堆上开辟的,系统并不会主动释放,如果我们也不释放,就会造成内存泄漏,所以这里我们在销毁函数中主动通过free函数释放指针指向的空间,并将指针置空,size和capacity置回0。

使用场景:

测试:

(3.2.3)打印函数—SLPrint

因为我们测试时经常需要打印动态顺序表中的数据,所以这里我们直接写一个打印函数方便后续的测试。因为我们前面写的SLDataType代表的是int类型的数据,所以这里的打印函数也是针对int类型的,如果想存储其他类型的数据并使用打印函数进行测试,可以将 printf () 中的%d进行更换。

这里size是动态顺序表中有效元素的个数,而动态顺序表底层是数组,下标是从0开始的,所以最后一个元素下标是size-1,所以循环条件如图中所示。

(3.3)检查扩容

在前面的初始化函数中,我们并没有为数组开辟空间,而是将指向数组的指针置空了,所以后续插入第一个数据时我们一定需要为数组开辟空间,而且如果数据量较大,已经开辟的空间存满了,我们还需要对原空间进行扩容。这里我们通过扩容函数解决这两个问题。

具体实现:

size代表动态顺序表的有效元素个数,capacity代表动态顺序表的总容量,当它们相等时,代表动态顺序表存满了,最开始size = capacity = 0,所以插入第一个数据时也会判断需要扩容,当判断需要扩容后,定义一个newcapacity表示扩容后的容量大小,通过一个三目运算符,如果capacity为0则给4,否则扩容到原空间的二倍(注意:capacity不是数组占多少个字节,而是数组最多能存储的当前类型的数据的个数)。然后通过realloc函数对空间进行扩容,这里扩容可能失败,如果用ps中的a指针接收,一旦扩容失败,函数返回空指针,那么连原来的空间也找不到了,所以先定义一个临时的指针,当判断指针不为空(即空间扩容成功)后,在将该地址赋回给管理动态顺序表中数组的指针(即ps中的a),同时更新动态顺序表容量。

(3.4)头部插入删除\尾部插入删除

(3.4.1)尾部插入

要往动态顺序表中插入数据,动态顺序表肯定要存在,所以先断言一下ps不可以为空(这里不一定非要用断言,用 if 语句判断也可以),然后检查一下动态顺序表是否需要扩容,最后将数据插入尾部即可,同时有效数据个数加1(注意:size是有效数据个数,所以原有数据的下标是从0到size - 1,所以数据插在尾部就是插在下标为size处)。具体过程可参考下图:

测试:

(3.4.2)尾部删除

只有顺序表存在且不为空的情况下才能进行删除,所以这里先对ps不为空和顺序表中的数据个数size不为0进行断言,然后直接将动态顺序表中的有效数据个数size减1就可以了,没有必要对删除的数据赋特殊值。具体过程可参考下图:

测试:

(3.4.3)头部插入

前面还是一样,断言指向动态顺序表的指针不为空,检查是否扩容,但是接下来如果直接将数据插入头部位置,那么原来的头部位置的数据将被覆盖,所以我们需要移动数据,将数组的起始位置空出来,然后在将数据插入,有效数据个数size加1。具体过程可参考下图:

测试:

(3.4.4)头部删除

只有顺序表存在且不为空的情况下才能进行删除,所以先断言ps指针不为空,size不为0,如果像尾删那样直接进行size-1,删除的是最后一个数据,所以要先进行数据移动,将头部后面的数据都向前移动一个位置,在进行size-1。具体过程可参考下图:

测试:

(3.5)指定位置插入\删除数据

(3.5.1)指定位置插入数据

插入数据首先动态顺序表要存在,其次插入的位置(这里的位置指的是数组下标)要合法,不能为负数,同时最远只能插入到数组最后一个有效数据的后面,所以pos还要小于等于size。这里还是用两个断言来控制这两个问题,然后检查是否需要扩容,在将pos位置及之后的数据向后移动一个位置,将pos位置空出来,最后将数据插入pos位置,并将有效数据个数size加1。具体过程可参考下图:

测试:

(3.5.2)指定位置删除数据

只有顺序表存在且不为空的情况下才能进行删除,并且指定删除的位置要合理,只能在有数据的位置进行删除,这里通过三个断言来控制上述条件,然后把要删除位置后面的数据向前移动一个位置,并将有效数据个数减1。具体过程可参考下图:

测试:

(3.6)查找指定数据

具体实现:

查找数据动态顺序表必须存在,但数据不一定在顺序表中,所以这里只需要断言一下ps不为空就可以,然后就是遍历当前顺序表中的数据,如果找到就返回下标,如果遍历已经结束还没有返回说明顺序表中没有该数据,那么返回-1。

测试:

四、通讯录项目

(4.1)通讯录功能

1)至少能够存储100个⼈的通讯信息
2)能够保存用户信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息

(4.2)动态顺序表的改变

因为通讯录项目存储的是联系人信息,而联系人信息需要用到结构体,所以要想复用前面写好的动态顺序表需要进行一定的改动。

(4.2.1)存储数据类型的改变

这里将SLDataType代表的数据从 int 改为存储联系人信息的结构体PersonInfo。

(4.2.2)打印函数的改变

因为动态顺序表存的是结构体,所以不能直接对顺序表中的数据进行打印,需要像图中一样,先通过数组下标访问到结构体,在访问结构体里的数据。

(4.2.3)查找函数的改变

同理,因为顺序表存储的是结构体,所以比较是否为相同元素时需要比较结构体里的每一个数据,这里存储联系人数据的结构体中的name,sex,tel,adder都是char类型数据的数组,比较是否相同需要用到strcmp函数,age是int类型的,直接比较就可以。

(4.2.4)添加SLSize函数

通过上述函数返回动态顺序表中有效数据个数。

(4.2.5)添加SLAt函数

该函数是用来修改指定位置的值的。在修改值之前要先断言一下顺序表不为空,并且修改位置合法。然后直接将数据放入该位置即可。

(4.3)通讯录基本结构定义

将前面定义好的动态顺序表重命名为contact,定义一个存储联系人信息的结构体,里面包含姓名—name,性别—sex,年龄—age,电话—tel,地址—addr,除了年龄是int类型其余都是char类型的数组,在前面通过宏定义出这些数组的大小。

(4.4)通讯录项目实现的函数

(4.4.1)初始化通讯录

初始化分为两个部分,第一部分是初始化动态顺序表,前面已经讲过了,第二部分是将文件中的数据导入动态顺序表中,如下图:

首先打开存储联系人信息的文件(这个文件需要我们自己创建好),当打开成功后,先创建一个用来存储联系人信息的临时变量,通过fread函数将读到的信息放到临时变量里,再将临时变量的信息放到动态顺序表中,再去读取下一个人的信息,重复上述操作,直到所有联系人信息都被放到动态顺序表中。

(4.4.2)添加通讯录数据

先创建一个临时变量存储要添加的联系人信息,然后通过打印提示信息让用户输入,并将读取到的用户输入放到临时变量中,当所有信息都录入完毕后将信息尾插到动态顺序表中,并提示用户插入成功。

(4.4.3)删除通讯录数据

删除通讯录数据主要通过联系人姓名,当用户输入要删除的联系人姓名后,通过FindByName方法找到要删除的联系人在动态顺序表中存储的位置(即在数组中的下标),如果用户存在,调用顺序表的删除方法将其删除。

FindByName:

在该函数中,我们需要遍历动态顺序表并比较联系人姓名,strcmp函数比较如果相等会返回0,找到后返回下标,否则返回-1。

(4.4.4)展示通讯录数据

该函数只需要遍历打印就可以了,在打印联系人数据前先打印提示信息,使用户知道展示的都是哪些信息。效果如图:

(4.4.5)查找通讯录数据

查找也是通过联系人姓名,当用户输入联系人姓名后通过复用FindByName函数进行查找,如果找到通过返回的下标打印其所有信息。效果如图:

(4.4.6)修改通讯录数据

修改也是通过联系人姓名,用户输入要修改的联系人姓名,复用FindByName函数进行查找,如果要修改的联系人是存在的,打印提示信息并让用户输入直接覆盖原内容。

(4.4.7)销毁通讯录数据

销毁分为两部分,先将动态顺序表中的联系人信息放入文件中,再销毁动态顺序表。SaveContact函数具体实现如下图:

(4.5)测试

测试中写了一个菜单函数,菜单的打印,通讯录的创建,初始化,以及各个接口的调用都在这个函数中完成,这里选择使用do—while循环,因为不管用户想进行何种操作,菜单一定会先打印一次,然后用户选择操作,定义一个变量op接受用户要进行的操作,通过switch—case跳转到相应的接口,输入不合法时会提醒重新输入。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/880592.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue中使用exceljs和file-saver插件实现纯前端表格导出Excel(支持样式配置,多级表头)

实现:使用Excel.js库创建excel文件,然后再使用 file-saver库将 Excel 文件保存到用户的本地计算机。 1.安装,可以使用npm,yarn npm install exceljs npm install file-saver 2.封装生成excel的方法 // 封装exceljs const ExcelJ…

H5 随机身份证号码、社会统一信用代码、手机号、名字、银行卡号码

平时注册可能会用到这些,原本用自己服务器搭了个,感觉纯前端的还能引入到项目中挺好的,之后再追加一些常用的随机数据,这样写表单应该就会好测试了(●’◡’●)。 在线链接 https://linyisonger.github.io/H5.Examples/?name./07…

Brave编译指南2024 MacOS篇-初始化构建环境(五)

引言 在上一篇文章中,我们完成了Brave浏览器编译所需的环境配置。现在,我们将进入下一个关键阶段:初始化构建环境。这个步骤将确保我们有一个完整的、准备就绪的开发环境,包括所有必要的依赖项和Chromium源代码。 1. 切换工作目…

上海沪鑫餐饮供应链:嘉定蔬菜配送分拣工作的精细艺术

在餐饮行业的背后,有一条默默运作的关键链条——餐饮供应链。而在上海沪鑫餐饮管理有限公司(简称沪鑫餐饮供应链、沪鑫食材)中,嘉定蔬菜配送分拣工作犹如一场精心编排的舞蹈,每一个动作都决定着最终呈现在餐桌上的美食…

文件和目录

文件和目录 获取文件属性 通过 ls 命令可以查看到文件的很多属性内容&#xff0c;这些文件属性的内容可以通过以下几个函数获取: #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>int stat(const char *pathname, struct stat *statbuf…

ISA Server配置https踩坑全过程

首先普通的http配置请参考 【ISA Server 2006发布Web网站】 https://www.bilibili.com/video/BV1qc411v75w/?share_sourcecopy_web&vd_sourcef35b2f2d0d34140bcba81b8b6f8c1b69 本文只一笔带过&#xff0c;讲一下https部分。 正解 由于我维护的是windows server 2003的…

计算机毕业设计 招生宣传管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

C语言扫盲

文章目录 C版本C语言特征GCCprintf数据类型函数指针内存管理void指针 Struct结构和Union结构typedef预处理器make工具cmake工具Projectintegral of sinc functionemulator embedded systeman event schedule 补充在线Linux终端安装Linux参考 建议还是国外教材学习…人家的PPT比…

物联网系统中OLED屏主流驱动方案详解

01 物联网系统中为什么要使用OLED驱动芯片 卓越的显示效果 1、高对比度和鲜艳色彩&#xff1a;OLED屏幕能够自发光&#xff0c;因此能够实现极高的对比度和鲜艳的色彩表现&#xff0c;这在物联网设备的显示界面上尤为重要&#xff0c;可以为用户提供更清晰、更生动的视觉体验…

[论文精读]TorWard: Discovery, Blocking, and Traceback of Malicious Traffic Over Tor

期刊名称&#xff1a;IEEE Transactions on Information Forensics and Security 发布链接&#xff1a;TorWard: Discovery, Blocking, and Traceback of Malicious Traffic Over Tor | IEEE Journals & Magazine | IEEE Xplore 中文译名&#xff1a;TorWard&#xff1a;…

jvm专题 之 内存模型

文章目录 前言一个java对象的运行过程jvm内存分布程序的基本运行程序对象什么是对象对象的创建一、类加载检查二、对象内存分配三、初始化零值四、设置对象头五、执行初始化方法 对象的访问定位 对象与类的关系由类创建对象的顺序 对象的创建 前言 一个程序需要运行&#xff0…

JNI实现Java调用C++函数

1. 测试环境 操作系统&#xff1a;win10JDK版本&#xff1a;JDK11 安装教程gcc版本&#xff1a;8.1.0 2. 声明native方法 // HelloJNI.java public class HelloJNI {// 输出Hello JNI from CPP. private native static void sayHello();// 实现两个整数相加private native s…

Android手机投屏方案实现方式对比

文章目录 1.概述2.术语解释2.1 miracast2.2 scrcpy2.4 Wifi Direct2.5 app_process 3.技术实现对比3.1 Miracast3.1.1 Miracast介绍3.1.2 Miracast原理3.1.3 Miracast优缺点分析 3.2 Scrcpy3.2.1 scrcpy 介绍3.2.2 scrcpy的实现原理3.2.3 scrcpy的优缺点分析 3.3 Google cast3.…

【学习笔记】手写 Tomcat 四

目录 一、Read 方法返回 -1 的问题 二、JDBC 优化 1. 创建配置文件 2. 创建工具类 3. 简化 JDBC 的步骤 三、修改密码 优化返回数据 创建修改密码的页面 注意 测试 四、优化响应动态资源 1. 创建 LoginServlet 类 2. 把登录功能的代码放到 LoginServlet 类 3. 创…

Python 算法交易实验89 QTV200日常推进-模式思考

说明 过去几天大A的表现还是比较戏剧化的。 让我想到了&#xff1a; 1 价稳量缩模式。之前很长一段时间都是这种状态&#xff0c;最终还是大爆发了&#xff0c;这个可取。2 周期模式。假设价格是一个周期为T(T可变)的正弦波&#xff0c;所以最终还是回到了几个月前的位置&…

UE学习篇ContentExample解读------Blueprints Advanced-下

文章目录 总览描述批次阅览2.1 Timeline animation2.2 Actor tracking2.3 Button Trigger using a blueprint interface2.4 Opening door with trigger2.5 Child Blueprints 概念总结致谢&#xff1a; 总览描述 打开关卡后&#xff0c;引入眼帘的就是针对关卡的总体性文字描述&…

物联网系统中基于IIC通信的数字温度传感器测温方案

01 物联网系统中为什么要使用数字式温度传感器芯片 物联网系统中使用数字式温度传感器芯片的原因主要有以下几点&#xff1a; 高精度与稳定性 高精度测量&#xff1a;数字式温度传感器芯片&#xff0c;如DS18B20&#xff0c;采用芯片集成技术&#xff0c;能够有效抑制外界不…

算法宝典——二分查找算法

1.认识二分查找 二分查找的时间复杂度:O(logN) 二分查找属于算法中耳熟能详的一类&#xff0c;通常的我们会说只有数组有序才可以使用二分查找&#xff0c;不过这种说法并不完全正确&#xff0c;只要数据具有"二段性"就可以使用二分查找&#xff0c;即我们可以找出一…

【零散技术】Odoo PDF 打印问题问题合集

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 Odoo PDF打印 是一个必备功能&#xff0c;但是总会遇到一些奇奇怪怪的问题&#xff0c;此帖仅做记录&#xff0c;方便查阅。 目录 1、样式丢失 2、部分结构丢失 3、没有中文字体 1、样式丢失 这种情况一般是由于 …

ppt压缩有什么简单方法?压缩PPT文件的几种方法

ppt压缩有什么简单方法&#xff1f;许多用户常常面临文件过大的问题&#xff0c;尤其在需要通过电子邮件发送或上传至网络平台时&#xff0c;大文件会带来诸多麻烦。此外&#xff0c;较大的文件可能导致软件响应缓慢&#xff0c;从而影响整体的演示体验。因此&#xff0c;寻找有…