C语言:顺序表的实现和通迅录项目实现

2024-08-21 03:20

本文主要是介绍C语言:顺序表的实现和通迅录项目实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

顺序表

1、顺序表的概念及结构

1.1 线性表

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

2、顺序表分类

• 顺序表和数组的区别
◦ 顺序表的底层结构是数组,对数组的封装实现了常⽤的增删改查等接⼝
• 顺序表分类

2.1◦ 静态顺序表

概念:使⽤定⻓数组存储元素
e0eb1881ab98446fb785c4215be81546.png
静态顺序表缺陷:空间给少了不够⽤,给多了造成空间浪费

2.2 动态顺序表

不使用定长数组,定义数组的地址。

67450bf375f94a249f5a5c328914fbe2.png
增容一般是以原来空间的两倍到三倍来增容。

3、动态顺序表的实现

3.1初始化和销毁

初始化:

只需要顺序表的数组地址置为空,然后让有效数据个数和空间容量初始化为零即可。

顺序标的销毁:

则是将顺序表内数组的空间释放掉(销毁之前要判空),同时,要指向该数组的指针指为空防止其成为野指针。后让有效数据个数和空间容量指为零即可。

void SLInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;
}void SLDestroy(SL* ps)
{assert(ps->arr != NULL);free(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}

3.2动态顺序表 -- 按需申请(扩容)

因为初始化时没有对顺序表申请空间,所以,要先判断空间是否等于零,如果是零的话,就给它赋一个初值,如果不为零的话,那就让它变为原来的两倍。

注意:申请来的空间,不能直接传给顺序表,因为空间有可能申请失败,如果这时直接给的话,就会导致数据丢失。

void SLCheckCapacity(SL* ps)
{if (ps->capacity == ps->size){int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLdataType* tmp = (SLdataType*)realloc(ps->arr, newCapacity * sizeof(SLdataType));assert(tmp != NULL);ps->arr = tmp;ps->capacity = newCapacity;}
}

3.3尾部插⼊删除

尾插:

插入之前要先判断传过要顺序表地址是否为空,和空间够不够,空间不够就要申请(这里调用可上面写的SLCeckCapacity(SL* ps)即可)然后直接插入到最后一个元素的位置即可。

尾删:

删要先判断顺序地址是否为空,顺序表里面元素个数不为零。尾删只需要把有效数据个数减一就行。

void SLPushBack(SL* ps, SLdataType x)
{assert(ps!= NULL);SLCheckCapacity(ps);ps->arr[ps->size] = x;ps->size++;
}void SLPopBack(SL* ps)
{assert(ps!= NULL);assert(ps->size > 0);ps->size--;
}

3.4头部插⼊删除

头插:

头删同样需要判空跟判空间够不够。

因为顺序表里面的元素在物理上是相邻,所以我们在插入第一个元素的时候,要把后面的元素全部往后移,然后再把元素插入空出来的第一个位置里面。

头删:

是把后面的元素全部往前挪一个位置,把第一个元素覆盖掉,再把有效数据减1就行了

void SLPushFront(SL* ps, SLdataType x)
{assert(ps != NULL);SLCheckCapacity(ps);for (int i = ps->size;i>0;i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}void SLPopFront(SL* ps)
{assert(ps != NULL);assert(ps->size > 0);for (int i = 0;i<ps->size-1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

3.5指定位置之前插⼊/删除数据

插入:

插入之前先看空间够不够和顺序表是否为空

然后只需要将指定位置及其之后的全部元素往后挪一位,再把元素插入到这个指定位置即可。

删:

删之前先判有无元素跟顺序表表是否为空。然后将指定位置之后的数据往前挪动1个元素位置,把要删除的元素覆盖,再将有效元素个数减一即可。

void SLInsert(SL* ps, int pos, SLdataType x)
{assert(ps != NULL);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size;i>pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}void SLErase(SL* ps, int pos)
{assert(ps != NULL);assert(pos >= 0 && pos < ps->size);for (int i =pos;i<ps->size-1;i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

4.顺序表的应⽤-- 基于动态顺序表实现通讯录

C语⾔基础要求:结构体、动态内存管理、顺序表、⽂件操作
1、功能要求
1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息

4.1.文件的布置和通讯录结构

上面我们实现的顺序表代码定义放在SeqList.c
声明放在SeqList.h。同时又新建两个文件,一个是Contact.h通讯录实现函数的声明,另一个Contact.c放通讯录函数的实现,再创建一个主函数文件test.c
7ca4dfc6646949dab6b3b4d5dcee7bd0.png
Contact.h:

4.2通讯录的初始化和销毁

就是对顺序表的初始化和销毁,是所以直接调用初始化和销毁函数即可。
//通讯录的初始化
void ContactInit(Contact* con)
{SLInit(con);
}//通讯录的销毁
void ContactDesTroy(Contact* con)
{SLDestroy(con);
}

4.3通讯录添加数据

这里可以直接声明,联系人结构体第一局部变量,然后把数据写进去,在调用顺序表中,尾插函数,把这个结构体直接插入即可。
//通讯录添加数据
void ContactAdd(Contact* con)
{//获取用户输入的内容:姓名+性别+年龄+电话+地址peoInfo info;printf("请输入要添加的联系人姓名:\n");scanf("%s", info.name);printf("请输入要添加的联系人性别:\n");scanf("%s", info.gender);printf("请输入要添加的联系人年龄:\n");scanf("%d", &info.age);printf("请输入要添加的联系人电话:\n");scanf("%s", info.tel);printf("请输入要添加的联系人住址:\n");scanf("%s", info.addr);//往通讯录中添加联系人数据SLPushBack(con, info);
}

4.4查找删除显⽰联系⼈信息

剩下的功能实现都要先查找到数据元素,这里写一个基于名字的查找函数函数,如果找到就把下标返回,否则返回-1。

int FindByName(Contact* con, char name[])
{for (int i = 0; i < con->size; i++){if (0 == strcmp(con->arr[i].name, name)){//找到了return i;}}//没有找到return -1;
}

删除数据之前要先找到该元素,所以调用一下,上面写,查找函数FindByName。如果该下表有意义,那直接将该下表传进之前写的顺序表指定位置删除函数,就能把该数据删除了。

而顺序表的修改差不多,先找到该元素,利用下表,直接访问修改该数据就行了

//通讯录删除数据
void ContactDel(Contact* con)
{char name[NAME_MAX];printf("请输入要删除数据联系人的姓名:\n");scanf("%s", name);int ret=FindByName(con, name);if (ret != -1){SLErase(con, ret);printf("删除成功\n");return;}elseprintf("该联系人不存在\n");
}//通讯录的修改
void ContactModify(Contact* con)
{char name[NAME_MAX];printf("请输入要修改数据联系人的姓名:\n");scanf("%s", name);int ret = FindByName(con, name);if (ret != -1){printf("请输入新的联系人姓名:\n");scanf("%s", con->arr[ret].name);printf("请输入新的联系人性别:\n");scanf("%s", con->arr[ret].gender);printf("请输入新的联系人年龄:\n");scanf("%d", &con->arr[ret].age);printf("请输入新的联系人电话:\n");scanf("%s", &con->arr[ret].tel);printf("请输入新的联系人住址:\n");scanf("%s", con->arr[ret].addr);}elseprintf("该联系人不存在\n");
}

这里查找也是先调用上面的那个查找函数FindByName。然后将要查找的数据源打印出来。
展示全部数据函数,又for循环将它们的个一个打印出来就行
//查找
int ContactFind(Contact* con)
{char name[NAME_MAX];printf("请输入要查找数据联系人的姓名:\n");scanf("%s", name);int ret = FindByName(con, name);if (ret != -1){printf("%3s %3s %3d %3s %3s \n",con->arr[ret].name, con->arr[ret].gender,con->arr[ret].age,con->arr[ret].tel,con->arr[ret].addr);return ret;}elseprintf("联系人不存在\n ");return -1;
}void ContactShow(Contact* con)
{if (con->size == 0){printf("联系人不存在\n ");return;}printf("%3s %3s %3s %3s %3s \n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < con->size; i++){printf(" %3s  %3s  %3d  %3s  %3s \n",con->arr[i].name,con->arr[i].gender,con->arr[i].age,con->arr[i].tel,con->arr[i].addr);}}

信息持久化,这里的调用二进制文件函数实现。

写的时候,我们是将一整块联系人数据用for循环写入。

读文件:

一边读一边调用顺序表尾插函数。就可以写入上一次的完整信息了。

//信息持久化
void SvaeContact(Contact* con)
{FILE* pf = fopen("Contact.txt", "wb");if (pf == NULL){perror("Contact.txt\n");return;}peoInfo info;for (int i = 0; i < con->size; i++){fwrite(con->arr + i, sizeof(peoInfo), 1, pf);}fclose(pf);pf = NULL;
}void LoadContact(Contact* con)
{FILE* pf = fopen("Contact.txt", "rb");if (pf == NULL){perror("Contact.txt\n");return;}peoInfo info;while (fread(&info, sizeof(peoInfo), 1, pf) != 0){SLPushBack(con,info);}printf("历史数据读取成功\n");fclose(pf);pf = NULL;
}

完整文件在开头,需要的自取

这篇关于C语言:顺序表的实现和通迅录项目实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1091973

相关文章

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

SpringBoot实现RSA+AES自动接口解密的实战指南

《SpringBoot实现RSA+AES自动接口解密的实战指南》在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题,RSA+AES混合加密方案因其安全性高、性能优越而被广泛采用,本... 目录一、项目依赖与环境准备1.1 Maven依赖配置1.2 密钥生成与配置二、加密工具类实现2.1

在Java中实现线程之间的数据共享的几种方式总结

《在Java中实现线程之间的数据共享的几种方式总结》在Java中实现线程间数据共享是并发编程的核心需求,但需要谨慎处理同步问题以避免竞态条件,本文通过代码示例给大家介绍了几种主要实现方式及其最佳实践,... 目录1. 共享变量与同步机制2. 轻量级通信机制3. 线程安全容器4. 线程局部变量(ThreadL

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我