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

数据结构(初阶):顺序表实战通讯录

前言 

数据结构(初阶)第一节:数据结构概论-CSDN博客

数据结构(初阶)第二节:顺序表-CSDN博客

        本文将以C语言和顺序表实现通讯录基础管理,实现功能包括增、删、改、查等,在实现相关功能时需要用到在第二节中顺序表的相关内容,需要友友们掌握顺序表的相关内容以及函数的实现方式。

要用到的两个文件

SeqList.h

//.h文件定义
#include "Contact.h"//头文件互相包含会报错
#include <malloc.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>typedef peoInfo SLDataType;//定义顺序表
typedef struct SeqList
{SLDataType* a;//数组int size;//有效元素int capacity;//容量
}SL;//初始化
void SLinit(SL* p1);//销毁
void SLdestory(SL* p1);//扩容
void SLcheckCapcity(SL* p1);//尾插
void SLpushBack(SL* p1, SLDataType x);//打印顺序表
void SLprint(SL* p1);//头插
void SLpushFront(SL* p1, SLDataType x);//尾删
void SLpopBack(SL* p1);//头删
void SLpopFront(SL* p1);//指定插入
void SLinsert(SL* p1, int pos, SLDataType x);//指定删除
void SLerase(SL* p1, int pos);//查询
//int SLfind(SL* p1, SLDataType x);

SeqList.c

#include "SeqList.h"//初始化
void SLinit(SL* p1)
{p1->a = (SLDataType*)malloc((sizeof(SLDataType)) * 4);if (p1->a == NULL){perror("malloc fail");return;}p1->capacity = 4;p1->size = 0;
}//销毁
void SLdestory(SL* p1)
{free(p1->a);p1->a = NULL;p1->capacity = 0;p1->size = 0;
}//扩容
void SLcheckCapcity(SL* p1)
{if (p1->size >= p1->capacity){SLDataType* tmp = (SLDataType*)realloc(p1->a, sizeof(SLDataType) * p1->capacity * 2);if (tmp == NULL){perror("realloc fail");return;}p1->a = tmp;p1->capacity *= 2;}
}//尾插
void SLpushBack(SL* p1, SLDataType x)
{assert(p1);SLcheckCapcity(p1);//检查是否需要扩容p1->a[(p1->size)++] = x;//在size处插入数据
}//打印顺序表
void SLprint(SL* p1)
{for (int i = 0; i < p1->size; i++){printf("%d\n", p1->a[i]);}
}//头插
void SLpushFront(SL* p1, SLDataType x)
{assert(p1);SLcheckCapcity(p1);for (int i = p1->size; i > 0; i--){p1->a[i] = p1->a[i - 1];}p1->a[0] = x;p1->size++;
}//尾删
void SLpopBack(SL* p1)
{assert(p1);assert(p1->size);//顺序表不为空//p1->a[p1->size - 1] = -1;p1->size--;
}//头删
void SLpopFront(SL* p1)
{assert(p1);assert(p1->size);for (int i = 1; i < p1->size; i++){p1->a[i - 1] = p1->a[i];}p1->size--;
}//指定下标添加
void SLinsert(SL* p1, int pos, SLDataType x)
{//要注意p1->size指向的是最后一个有效数据的下一位//pos是指定的插入位置的下标(如果为0则是头插,如果为ps->size-1则为尾插)//x是待插入的数据assert(p1 && pos >= 0 && pos < p1->size);SLcheckCapcity(p1);for (int i = p1->size; i > pos; i--){p1->a[i] = p1->a[i - 1];}p1->a[pos] = x;p1->size++;
}//指定下标删除
void SLerase(SL* p1, int pos)
{assert(p1 && pos >= 0 && pos < p1->size);for (int i = pos; i < p1->size - 1; i++){p1->a[i] = p1->a[i + 1];}p1->size--;
}//查询
//int SLfind(SL* p1, SLDataType x)
//{
//	assert(p1);
//	for (int i = 0; i < p1->size; i++)
//	{
//		if (p1->a[i] == x)
//		{
//			return i;//找到后返回下标
//		}
//	}
//	return -1;//没有找到返回-1
//}

正文

文件包含关系

在实现通讯录的工程文件中一共包含了5个子文件,分别是

  • test.c:用于在编写过程中测试代码能否正常运行
  • SeqList.h:用于在实现顺序表的过程中定义结构体和各种方法
  • SeqList.c:用于实现在头文件中定义的方法
  • Contact.h:定义通讯录中实现功能的函数
  • Contact.c:实现头文件中定义的函数

        通讯录实质上就是顺序表,只不过是改了名字(换汤不换药),我们只需要在实现顺序表的基础上给他起个别名通讯录(Contact)即可。

        在顺序表中,数组中存储的是单一的元素,在通讯录中,原数组中的元素变成了存储联系人数据的结构体(personInfo),数组中的每个元素都是结构体类型,包括姓名、电话、性别、住址等,本质上是两个结构体的嵌套。

在Contact.h中我们定义好联系人结构体和要用到的方法

#define NAME_MAX 20
#define GENDER_MAX 5
#define PHONE_MAX 20
#define ADDS_MAX 20typedef struct personInfo
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char phoneNum[PHONE_MAX];char adds[ADDS_MAX];
}peoInfo;//前置声明
typedef struct SeqList Contact;//将顺序表命名为"通讯录"//菜单
void menu();//初始化
void ContactInit(Contact* p);//销毁
void ContactDestory(Contact* p);//添加
void ContactAdd(Contact* p);//删除
void ContactDle(Contact* p);//修改
void ContactModify(Contact* p);//查找
void ContactFind(Contact* p);//显示
void ContactShow(Contact* p);

        typedef struct SeqList Contact;在这一句代码中使用前置声明将Seqlist重命名为Contact,在该文件中我们并没有定义结构体SeqList,使用前置声明只是为了让编译器知道有这个结构体的存在,而无法直接对之前重命名过的SL(typedef struct SeqList SL;)再命名的原因是编译器不能识别到SL的存在,如果想要识别必须包含"SeqList.h",但是头文件相互包含会导致报错,后面会讲到。

        在SeqList.h中将SLDateType自定义类型更改为perInfo,需要将"Contact.h"包含进文件,不能将"SeqList.h"同时包含进Contact.h中,这样会导致程序报错。

typedef peoInfo SLDataType;//定义顺序表
typedef struct SeqList
{SLDataType* a;//数组int size;//有效元素int capacity;//容量
}SL;

        在Contact.h中对SeqList重命名,在SeqList.h中更改自定义数据类型,此时我们通过Contact*p和SL*p定义的两种结构体指针都会被程序正确识别,本质上Contact*p等价于SL*p。

Contact.h

#define NAME_MAX 20
#define GENDER_MAX 5
#define PHONE_MAX 20
#define ADDS_MAX 20typedef struct personInfo
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char phoneNum[PHONE_MAX];char adds[ADDS_MAX];
}peoInfo;//前置声明
typedef struct SeqList Contact;//将顺序表命名为"通讯录"//菜单
void menu();//初始化
void ContactInit(Contact* p);//销毁
void ContactDestory(Contact* p);//添加
void ContactAdd(Contact* p);//删除
void ContactDle(Contact* p);//修改
void ContactModify(Contact* p);//查找
void ContactFind(Contact* p);//显示
void ContactShow(Contact* p);

Contast.c

头文件

#include "Contact.h"
#include "SeqList.h"

菜单

void menu()
{printf("-----------------------\n");printf("        1.添加        \n");printf("        2.删除        \n");printf("        3.查找        \n");printf("        4.显示        \n");printf("        5.修改        \n");printf("        0.退出        \n");printf("-----------------------\n");
}

初始化

//初始化
void ContactInit(Contact* p)
{SLinit(p);//直接调用已经在SeqList.c中实现好的初始化函数即可
} 

销毁

void ContactDestory(Contact* p)
{SLdestory(p);
}

添加

void ContactAdd(Contact* p)
{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.phoneNum);printf("请输入联系人的住址:\n");scanf("%s", info.adds);SLpushBack(p, info);//这里选择尾插printf("添加成功!\n\n");}

判断名字是否存在

int FindName(Contact* p, char* name)
{for (int i = 0; i < p->size; i++){if (strcmp(p->a[i].name, name) == 0)return i;//返回下标}return -1;
}

删除

void ContactDle(Contact* p)
{char n[NAME_MAX];printf("请输入要删除的联系人姓名:\n");scanf("%s", n);int ret = FindName(p, n);if (ret < 0){printf("删除对象不存在!\n");return;}SLerase(p, ret);printf("删除成功!\n");
}

显示

void ContactShow(Contact* p)
{printf("%-15s%-15s%-15s%-15s%-15s\n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < p->size; i++){printf("%-15s%-15s%-15d%-15s%-15s\n", p->a[i].name, p->a[i].gender, p->a[i].age,p->a[i].phoneNum, p->a[i].adds);}
}

修改

void ContactModify(Contact* p)
{char name[NAME_MAX];printf("请输入要修改的联系人姓名:\n");scanf("%s", name);int ret = FindName(p, name);if (ret < 0){printf("修改对象不存在!\n");return;}printf("请输入新的姓名:\n");scanf("%s", p->a[ret].name);printf("请输入新的性别:\n");scanf("%s", p->a[ret].gender);printf("请输入新的年龄:\n");scanf("%d", &p->a[ret].age);printf("请输入新的电话:\n");scanf("%s", p->a[ret].phoneNum);printf("请输入新的地址:\n");scanf("%s", p->a[ret].adds);printf("修改成功!\n\n");
}

查找

void ContactFind(Contact* p)
{char name[NAME_MAX];printf("请输入要查找的联系人姓名:\n");scanf("%s", name);int ret = FindName(p, name);if (ret < 0){printf("联系人不存在!\n");return;}printf("%-15s%-15s%-15s%-15s%-15s\n", "姓名", "性别", "年龄", "电话", "地址");printf("%-15s%-15s%-15d%-15s%-15s\n", p->a[ret].name, p->a[ret].gender, p->a[ret].age,p->a[ret].phoneNum, p->a[ret].adds);printf("查询成功!\n\n");
}

测试文件test.c

#include "SeqList.h"int main()
{Contact con;ContactInit(&con);while (1){menu();int i = 0;printf("请选择你的操作:");scanf("%d", &i);switch (i){case 1:ContactAdd(&con);break;case 2:ContactDle(&con);break;case 3:ContactFind(&con);break;case 4:ContactShow(&con);break;case 5:ContactModify(&con);break;case 0:printf("程序已退出!\n");break;}}return 0;
}

 

相关文章:

数据结构(初阶):顺序表实战通讯录

前言 数据结构&#xff08;初阶&#xff09;第一节&#xff1a;数据结构概论-CSDN博客 数据结构&#xff08;初阶&#xff09;第二节&#xff1a;顺序表-CSDN博客 本文将以C语言和顺序表实现通讯录基础管理&#xff0c;实现功能包括增、删、改、查等&#xff0c;在实现相关功能…...

Outlook会议邀请邮件在答复后就不见了

时常会有同事找到我说&#xff0c;Outlook答复会议邀请邮件后收件箱就找不到会议邀请的邮件了。 这其实是Outlook的的一个机制&#xff0c;会把应答后的会议邀请邮件从收件箱自动删除&#xff0c;到已删除的邮件那里就能找到。如果不想要自动删除&#xff0c;改一个设置即可。…...

【C++】list模拟实现

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. list源码3. 初始化3.1 构造3.2 拷贝构造3.3 赋值3.4 析构 4. 迭代器4.1 后置加加和前置加加4.2 后置减减和前置减减4.3 解引用4.4 &#xff01;和4.5 begin 和 end4.6 const迭代器4.7 迭代器优化 5. Modifi…...

ETL工具-nifi干货系列 第八讲 处理器PutDatabaseRecord 写数据库(详细)

1、本节通过一个小例子来讲解下处理器PutDatabaseRecord&#xff0c;该处理器的作用是将数据写入数据库。 如下流程通过处理器GenerateFlowFile 生成数据&#xff0c;然后通过处理器JoltTransformJSON转换结构&#xff0c;最后通过处理器PutDatabaseRecord将数据写入数据库。如…...

【MySQL】如何判断一个数据库是否出问题

在实际的应用中&#xff0c;其实大多数是主从结构。而采用主备&#xff0c;一般都需要一定的费用。 对于主备&#xff0c;如果主机故障&#xff0c;那么只需要直接将流量打到备机就可以&#xff0c;但是对于一主多从&#xff0c;还需要将从库连接到主库上。 对于切换的操作&a…...

SQLite数据库的性能问题并不是单纯地由数据量的大小决定的,而是受到多种因素的综合影响。以下是一些可能导致SQLite性能问题的因素

SQLite数据库的性能问题并不是单纯地由数据量的大小决定的&#xff0c;而是受到多种因素的综合影响。以下是一些可能导致SQLite性能问题的因素&#xff1a; 数据量&#xff1a;当SQLite数据库中的数据量增长到一定程度时&#xff0c;查询、插入和更新等操作可能会变得缓慢。这…...

Blender怎么样启动默认移动和Cavity效果

在使用Blender的过程中&#xff0c;有一些特殊的技巧很重要。 比如默认地设置blender打开时&#xff0c;就是移动物体&#xff0c;这样怎么样设置的呢&#xff1f; 需要在界面里打开下面的菜单: 这样就找到默认设置的地方&#xff0c;把下面的移动勾选起来&#xff0c;这样点…...

Android 解决TextView多行滑动与NestedScrollView嵌套滑动冲突的问题

关键计算地方: 1.当前是上滑动还是下滑动(相对于屏幕) ,使用ev.getRawY()获得当前滑动位置在屏幕哪个地方 2. 计算文本客滑动到哪里即可停止, (行高*总文本行数)- (行高 * 最多显示行数) int sum getLineHeight() * getLineCount() - getLineHeight() * getMaxLines(); …...

Laravel 开发Api规范

一&#xff0c;修改时区 配置 config/app.php 文件 // 时区修改&#xff0c;感觉两者皆可&#xff0c;自己根据实际情况定义 timezone > PRC, // 大陆时间二&#xff0c;设置 Accept 头中间件 accept头即为客户端请求头&#xff0c;做成中间件来使用。Accept 决定了响应返…...

蓝色wordpress外贸建站模板

蓝色wordpress外贸建站模板 https://www.mymoban.com/wordpress/7.html...

windos环境,使用docker容器运行项目的,新增外部访问地址配置

对于运行在 Docker 容器中的项目&#xff0c;你需要在容器内部编辑 resolv.conf 文件。以下是一种常见的方法&#xff1a; 进入正在运行的 Docker 容器&#xff1a;docker exec -it [container_id] bash其中 [container_id] 是你正在运行的 Docker 容器的 ID。 在容器内部使…...

设计模式:生活中的组合模式

想象一下&#xff0c;你正在组织一个大型的家庭聚会。在这个聚会中&#xff0c;你需要准备各种菜肴&#xff0c;每个菜肴又包含不同的食材。你的目标是能够以统一的方式处理整个聚会的准备工作&#xff0c;不论是处理单个食材还是一整道菜肴。 在这个场景中&#xff0c;我们可…...

WPF OnStartup

在Windows Presentation Foundation (WPF)框架中&#xff0c;OnStartup 是 System.Windows.Application 类的一个受保护的虚方法&#xff0c;它是应用程序启动过程中的一个重要环节。当一个 WPF 应用程序启动时&#xff0c;其入口点通常是 App.xaml 文件和对应的后台代码文件 A…...

docker-相关

打镜像 1、编写dockfile文件&#xff0c;请自行百度 2、docker build -t 镜像名称:版本号 dockerFile路径 3、docker save -o 镜像压缩包名称.tar 镜像名称:镜像版本号 部署镜像 1、将镜像tar包放到部署机器上 2、加载镜像&#xff1a;docker load -i 镜像tar包路径 3、dock…...

二十、Rust AOP 切面增强

用过 java spring 的同学&#xff0c;应该会对 AspectJ 的 前置、后置、环绕 增强 念念不忘&#xff0c;巧了 rust 也有类似能力&#xff0c;稍显不同的是&#xff0c;为了向 “零成本抽象” 靠齐&#xff0c;Rust 的 “增强” 是在编译期 完成的。 编译期生成&#xff0c;则离…...

掌握Go语言:Go语言精细错误,清晰、高效的错误处理实践(32)

错误处理是任何编程语言中都至关重要的一部分&#xff0c;Go 语言提供了一套简单而强大的错误处理机制&#xff0c;使得处理错误变得高效而清晰。 Go 错误类型 在 Go 中&#xff0c;错误是一个普通的接口类型&#xff0c;即 error 接口&#xff0c;其定义如下&#xff1a; t…...

Spring与Web环境的集成

1. ApplicationContext应用上下文获取方式 应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的&#xff0c;但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件) &#xff0c;这样的弊端是配置文件加载多…...

二叉树的遍历——bfs广度优先搜索

1、BinNode类的创建 &#xff08;1&#xff09;代码总览 ##&#xff08;2&#xff09;测试示例 2、二叉树的遍历 &#xff08;1&#xff09;图示 &#xff08;2&#xff09;代码总览 &#xff08;3&#xff09;测试示例...

飞鸟写作可靠吗 #职场发展#经验分享#经验分享

飞鸟写作是一个非常便捷的论文写作工具&#xff0c;不仅可以帮助用户高效地完成论文写作&#xff0c;还可以提供查重降重的功能&#xff0c;帮助用户确保论文的原创性。那么&#xff0c;飞鸟写作到底可靠吗&#xff1f;答案是肯定的。 首先&#xff0c;飞鸟写作提供的查重降重功…...

Java 实现自定义注解

一、interface 关键字 我们想定义一个自己的注解 需要使用 interface 关键字来定义。 如定义一个叫 MyAnnotation 的注解&#xff1a; public interface MyAnnotation { } 二、元注解 光加上 interface 关键字 还不够&#xff0c;我们还需要了解5大元注解 RetentionTargetDo…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...

RKNN开发环境搭建2-RKNN Model Zoo 环境搭建

目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程.   本…...

论文笔记:Large Language Models for Next Point-of-Interest Recommendation

SIGIR 2024 1 intro 传统的基于数值的POI推荐方法在处理上下文信息时存在两个主要限制 需要将异构的LBSN数据转换为数字&#xff0c;这可能导致上下文信息的固有含义丢失仅依赖于统计和人为设计来理解上下文信息&#xff0c;缺乏对上下文信息提供的语义概念的理解 ——>使用…...