数据结构之使用顺序表写出通讯录

前言

昨天我们踏入了数据结构的深山,并且和顺序表battle了一番,虽说最后赢了,但同时也留下了一个问题:如何从顺序表的增删查改加强到通讯录的的增删查改,别急,今天就带你一探究竟。

一.回顾与思考

我们昨天实现了顺序表的头删,头插,尾删尾插,选择插入,选择删除,以及初始化和销毁等功能,我们也知道通讯录其实就是之前的顺序表的plus版本,二者有很强的关联性,于是乎我们今天不打算从头开始写代码,而是在昨天的基础上进行修改和完善。

没看过的宝子建议先去了解一下哦

【数据结构之顺序表的增删查改 - CSDN App】http://t.csdnimg.cn/VK5iU

二.目标展示

 🌟 🌟 🌟在开始前,先来看看我们都要干什么吧 🌟 🌟 🌟

功能要求
1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息
8)程序结束后,历史通讯录信息不会丢失

三通讯录启动

老规矩,我先把头文件展示一下,然后挨个给王子公主们讲解

🌞SL.h

#pragma once
#include"contact.h"
#include<Windows.h>
#define _CRT_SECURE_NO_WARNINGS 1
typedef struct PersonInfo
{
    char name[NAME_MAX];
    char sex[SEX_MAX];
    int age;
    char tele[TEL_MAX];
    char address[ADDR_MAX];
}SLDataType;
typedef struct SL
{
    SLDataType* a;
    int size;
    int capacity;
}SL;
//typedef  contact SL;
//初始化
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLDelete(SL* ps, int pos);

🌞contact.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define NAME_MAX 100
#define SEX_MAX 10
#define TEL_MAX 11
#define ADDR_MAX 100
#include<stdio.h>
#include<string.h>
//前置声明
struct SL;
typedef struct SL contact;
//用户数据

//初始化通讯录
void InitContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);

与上次不同,这次我们写了两个头文件,一个是在原来顺序表的基础上修改的,一个是通讯录的头文件,别晕,咱们慢慢说。

🌞SL.h头文件讲解

🐻1.防止头文件被多次包含。

#pragma once

🐻2.把我们需要的头文件包含一下,contact.h中有我们需要的宏,至于windows.h,到后面你就知道了。

#include"contact.h"
#include<Windows.h>

🐻3.vs用户应该都知道这个,为了防止IDE报scanf等函数的错。

#define _CRT_SECURE_NO_WARNINGS 1

🐻4.联系人结构体定义

typedef struct PersonInfo
{
    char name[NAME_MAX];
    char sex[SEX_MAX];
    int age;
    char tele[TEL_MAX];
    char address[ADDR_MAX];
}SLDataType;
注意注意,重点来了,我们定义了一个结构体,它包含了通讯录联系人的种种信息例如姓名,性别,年龄,电话和住址,接着我们又给结构体换了个名字——SLDataType
它是个英文缩写,即Sequence List Data Type,顺序表数据类型,ok回想一下上一篇文章,我们所用的顺序表数据类型是int,当时也是给他换成了这个名字,如此一来,之前我写的函数就不会因为类型改变而要再全部改一遍了。

🐻5.通讯录结构体定义

typedef struct SL
{
    SLDataType* a;
    int size;
    int capacity;
}SL;
好的,紧跟着的又是一个结构体,它就是我们的通讯录类型,他有三个成员
第一个是存放联系人的个人信息的
第二个是通讯录当前的数据数量,
第三个是通讯录容量
与昨天相比,他只有SLDataType改变了
后面的就是咱们昨天写的函数了,我就不再赘述了

ok'接下来看contact.h 

🌞contact.h头文件讲解

🦊1.宏定义


#define NAME_MAX 100//
#define SEX_MAX 10//
#define TEL_MAX 11//
#define ADDR_MAX 100//
  • 1.姓名的最大长度
  • 2.性别单词的最大长度(female和male)
  • 3.电话号码最大长度
  • 4.地址的最大长度

🦊2.前置声明

struct SL;
typedef struct SL contact;
//用户数据
前置声明,等会我们要用到这个结构体,但这个结构提示定义在另一个头文件的,所以先声明一下
再给他换个名字(当然你也可以不换),主要是为了方便记忆,既然他是这个通讯录的类型,那干脆给他起名叫通讯录得了——contact。
注意注意,下面就要写函数了,重点来了
首先在强调一下,我们之前是写过了的,所以今天不用从头再来,我们要做事是 修改

🦊3.初始化通讯录


void InitContact(contact* con);
void InitContact(contact* con)
{
    SLInit(con);
}
这个就是正常开辟堆区空间就好。
现在我们先不急着往下写我们想想我们等下要写修改数据,删除数据,展示数据的函数,他们的共同点是都要先找到那个联系人才行,于是乎我们可以直接写出实现这个功能的函数,之后直接调用它就好了

🦊4.查找函数


int checkbyname(contact* con, char* p);
我们这次以名字来查找对象,通在or循环中一个个通过strcmp和目标的名字比较
找到了返回下标,否则返回-1
//查找函数
int checkbyname(contact* con, char* p)
{
    for (int i = 0; i < con->size; i++)
    {
        if (strcmp(p, con->a[i].name)==0)
            return i;
    }
    return -1;
}

🦊5.删除通讯录数据


void DelContact(contact* con);
  • 1.检查通讯录内容是否为空(都空了你还删个啥)
  • 2.通过scanf接受名字,记得把回车移除缓冲区
  • 3.调用查找函数找找这个人在哪里
  • 4.调用之前的指定删除函数
//删除通讯录数据
void DelContact(contact* con)
{
    if (con->size - 1 < 0)
    {
        printf("通讯录空了\n");
        //perror("SLPopFront Fail");
        return;
    }
    printf("输入你要删除的联系人姓名\n");
    char p[101];
    scanf("%s",p);
    getchar();
    int pos = checkbyname(con, p);
    if (pos == -1)
        printf("此人不存在\n");
    else
    {
        SLDelete(con, pos);
        printf("删除成功\n");
    }
}
void SLDelete(SL* ps, int pos)
{
    for (int i = pos; i < ps->size - 1; i++)
        ps->a[i] = ps->a[i + 1];
    ps->size--;
}

🦊6.添加通讯录数据

void AddContact(contact* con);
把你要添加的联系人信息输进去,存到一个SLDataType变量中
通过之前写的尾插把联系人添加进去
void AddContact(contact* con)
{
    SLDataType a;
    printf("请输入姓名\n");
    scanf("%s", a.name);
    getchar();
    printf("请输入性别\n");
    scanf("%s", a.sex);
    getchar();
    printf("请输入年龄\n");
    scanf("%d", &a.age);
    getchar();
    printf("请输入电话号码\n");
    scanf("%s", a.tele);
    getchar();
    printf("请输入地址\n");
    scanf("%s", a.address);
    getchar();
    SLPushBack(con,a);
}
void SLPushBack(SL* ps, SLDataType x)
{
    if (ps->size + 1 > ps->capacity)
        SLCheckCapacity(ps);
    ps->a[ps->size] = x;
    (ps->size)++;
}

🦊7.展示通讯录数据

void ShowContact(contact* con);
通过for循环把信息全部打印出来
void ShowContact(contact* con)
{
    printf("姓名\t性别\t年龄\t号码\t地址\n");
    for (int i = 0; i < con->size; i++)
    {
        printf("%s\t%s\t%d\t%s\t%s\n", con->a[i].name, con->a[i].sex, con->a[i].age, con->a[i].tele, con->a[i].address);
    }
}

🦊8.查找通讯录数据

void FindContact(contact* con);
  • 1.输入要找的人名
  • 2.调用查找函数
  • 3.打印出来你要找的人的名字
void FindContact(contact* con)
{
    char name[100] = { 0 };
    printf("输入你要找的名字\n");
    scanf("%s",name);
    getchar();
    int i=checkbyname(con, name);
    if (i == -1)
        printf("没有你要找的人\n");
    else
        printf("%s\t%s\t%d\t%s\t%s\n", con->a[i].name, con->a[i].sex, con->a[i].age, con->a[i].tele, con->a[i].address);
}

🦊9.修改通讯录数据

void ModifyContact(contact* con);
  • 1.输入名字
  • 2.调用查找函数
  • 3.输入你要修改为什么信息
  • 4.通过之前的选择插入函数修改数据
//修改通讯录数据
void ModifyContact(contact* con)
{
    char name[100] = { 0 };
    printf("输入你要修改的人的名字\n");
    scanf("%s",name);
    getchar();
    int i = checkbyname(con, name);
    if (i == -1)
        printf("没有你要找的人\n");
    else
    {
        SLDataType a;
        printf("请输入姓名\n");
        scanf("%s", a.name);
        getchar();
        printf("请输入性别\n");
        scanf("%s", a.sex);
        getchar();
        printf("请输入年龄\n");
        scanf("%d", &a.age);
        getchar();
        printf("请输入电话号码\n");
        scanf("%s", a.tele);
        getchar();
        printf("请输入地址\n");
        scanf("%s", a.address);
        getchar();
         SLInsert(con, i, a);
    }
}

🦊10.销毁通讯录数据

void DestroyContact(contact* con);
void DestroyContact(contact* con)
{
    SLDestroy(con);
    printf("退出中,请稍等\n");
    Sleep(1000);
}
诶喝,你想想,咱们退出而时候加上这句话“退出中,请稍等”,再用sleep函数暂停个1秒2秒,是不是更有那种感觉。
but,我们还没有结束,别忘了最后一条要求
🪐🪐​​​​​​​🪐​​​​​​​🪐 那看来文件操作是必不可少了🪐🪐🪐🪐🪐
其实也好想,退出的时候把数据保存在一个文件中,开始运行程序时把这文件的数据读取出来。
那我们就要修改初始化函数和销毁函数了
我们使用fread和fwrite
  • 方法一:我的思路是先把当前数据数量读进文件中,这样之后把这个数据再读出来就可以循环读取联系人信息了
  • 方法二是:利用fread的返回值,他的返回值使实际读取的数据数量,我们每次读一个,当返回值为0,就说明读完了,于是停止读取。
void SLInit(SL* ps)
{
    FILE* p = fopen("contact.x", "rb");
    if (p != NULL)
    {
        {
            fread(&ps->size, sizeof(int), 1, p);
            fread(&ps->capacity,  sizeof(int),1, p);
            ps->a = (SLDataType*)malloc(ps->capacity * sizeof(SLDataType));
            for (int i = 0; i < ps->size; i++)
            {
                fread(&ps->a[i], sizeof(SLDataType), 1, p);
            }
        }
        fclose(p);
    }
    else
    {
        ps->a = (SLDataType*)malloc(CAPACITY * sizeof(SLDataType));
        ps->size = 0;
        ps->capacity = 4;
    }
}

void SLDestroy(SL* ps)
{
    FILE* p = fopen("contact.x","wb");
    if (p == NULL)
        printf("保存数据失败\n");
    else

    {
        fwrite (&ps->size,sizeof(int),1,p);
        fwrite(&ps->capacity, sizeof(int),1, p);
        for (int i = 0; i < ps->size; i++)
        {
            fwrite(ps->a+i, sizeof(SLDataType),1, p);
        }
        fclose(p);
    }
    
    free(ps->a);
    ps->a = NULL;
    ps->size = 0;
    ps->capacity = 0;
}

最后的最后,把主函数奉上

#include"contact.h"
#include"SL.h"
void menu()
{
    printf("*********  通讯录  ***********\n");
    printf("***1.添加  *****  2.删除*********\n");
    printf("***3.展示  *****  4.查找********\n");
    printf("***5.修改  *****  0.退出并保存*****\n");
}
int main()
{
    int b = -1;
    contact a;
    InitContact(&a);
    do
    {
        menu();
        scanf("%d", &b);
        switch (b)
        {
        case 1:
            AddContact(&a);
            break;
        case 2:
            DelContact(&a);
            break;
        case 3:
            ShowContact(&a);
            break;
        case 4:
            FindContact(&a);
            break;
        case 5:
            ModifyContact(&a);
            break;
        case 0:
            DestroyContact(&a);
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    } while (b);
}

总结:

经过顺序表和通讯录的洗礼,我们算是熬过这数据结构第一关了,我的建议是趁热打铁去刷点顺序表的oj题来巩固一下。

那么下一站,单链表,我们再会!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

20.云原生之GitLab CICD实战

云原生专栏大纲 文章目录 GitLab RunnerGitLab Runner 介绍Gitlab Runner工作流程 Gitlab集成Gitlab RunnerGitLab Runner 版本选择Gitlab Runner部署docker-compose方式安装kubesphere中可视化方式安装helm方式安装 配置gitlab-runner配置gitlab-ci.ymlgitlab-ci.yml 介绍编写…

i18n多国语言Internationalization的动态实现

一、数据动态的更新 在上一篇i18n多国语言Internationalization的实现-CSDN博客&#xff0c;可能会遇到一个问题&#xff0c;我们在进行英文或中文切换时&#xff0c;并没有办法对当前的数据进行动态的更新。指的是什么意思呢&#xff1f;当前app.js当中一个组件内容&#xff…

Docker镜像操作

镜像名称 镜名称一般分两部分组成&#xff1a;[repository]:[tag]。 在没有指定tag时&#xff0c;默认是latest&#xff0c;代表最新版本的镜像。 这里的mysql就是repository&#xff0c;5.7就是tag&#xff0c;合一起就是镜像名称&#xff0c;代表5.7版本的MySQL镜像。 镜像…

基于Java开发的校园失物招领系统详细设计和实现【附源码】

基于Java开发的校园失物招领系统详细设计和实现【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制…

若依前后台分离vue项目放开前台页面拦截配置

若依前后台分离vue项目放开前台页面拦截配置 使用场景某些页面不需要权限就能直接访问的页面 , 例如做个单点登录之类的。这里只需要修改2处即可 ssologin.vue代码 <template> </template> <script> export default {name: "SsoLogin",data() {r…

软件设计师——软件工程(五)

&#x1f4d1;前言 本文主要是【软件工程】——软件设计师——软件工程的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

怎么提升数据分析能力?——功法篇(下)

先来复习一下上篇提到的3个疑问&#xff1a; 为什么我做出来的分析总觉得没有别人的那么高级&#xff1f; 老板为什么总说我的分析“太浅了”&#xff1f; 数据分析师每天的工作就是取数做需求&#xff1f; 看完上篇讲的金字塔原理&#xff0c;如果你还有疑问&#xff0c;不妨再…

Mac上如何设置映射某个网站站点域名的IP

最近某常用的站点换 IP 了&#xff0c;但是 DNS 服务器还没有修改&#xff0c;这就导致无法访问&#xff08;换 DNS 服务器也不行&#xff09;。在用了一段时间的 IP 访问之后&#xff0c;还是没好&#xff0c;不知道是 DNS 污染还是咋了&#xff0c;所以最后还是手动改一下吧。…

使用Docker部署Apache Superset结合内网穿透实现远程访问本地服务

文章目录 前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网穿透&#xff0c;实现公网访问3. 设置固定连接公网地址 前言 Superset是一款由中国知名科技公司开源的“现代化的…

Hypervisor 和Docker 还有Qemu有什么区别与联系?

Hypervisor Hypervisor是一种运行在基础物理服务器和操作系统之间的中间软件层&#xff0c;可以让多个操作系统和应用共享硬件资源&#xff0c;也叫做虚拟机监视器&#xff08;VMM&#xff09;。 Hypervisor有两种类型&#xff1a;Type I和Type II。 Type I 直接运行在硬件上&a…

matlab模型变量一般说明

注意我是用的是matlab2019b 1&#xff0c;输入标定量&#xff0c;使用constant&#xff0c;用cal函数包裹 2&#xff0c;输出显示量&#xff0c;在划线上标注&#xff0c;然后用display函数包裹&#xff0c; 第一步和第二步完成以后&#xff0c;生产标定量a2l 3&#xff0c;输入…

Windows下网络编程(win32API+VS2022)

一、开发环境 我这里介绍下我用的环境安装过程。 所有版本的VS都可以的。 我当前环境是在Windows下&#xff0c;IDE用的是地表最强IDE VS2022。 下载地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/downloads/ 因为我这里只需要用到C和C语言编程&#xff0c;那…

机器学习分类模型评价指标总结(准确率、精确率、召回率、Fmax、TPR、FPR、ROC曲线、PR曲线,AUC,AUPR)

为了看懂论文&#xff0c;不得不先学一些预备知识&#xff08;&#xff08;55555 主要概念 解释见图 TP、FP、TN、FN 准确率、精确率&#xff08;查准率&#xff09;、召回率&#xff08;查全率&#xff09; 真阳性率TPR、伪阳性率FPR F1-score2TP/(2*TPFPFN) 最大响应分…

【GitHub项目推荐--一个语音机器人项目】【转载】

推荐一个腾讯大佬开源的语音对话机器人&#xff1a;wukong-robot &#xff0c;悟空机器人在 GitHub 上斩获 3.2K 的 Star。 这是一个简单灵活的中文语音对话机器人项目&#xff0c;目的是让中国的开发者也能快速打造个性化的智能音箱&#xff0c;同时&#xff0c;该项目还是第…

Redis持久化方案RDB和AOF

Redis两种持久化方案 RDB持久化AOF持久化 RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&#xff0c;从磁盘读取快照文…

基于GA遗传优化的混合发电系统优化配置算法matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1遗传算法基本原理 4.2 混合发电系统优化配置问题 4.3 基于GA的优化配置算法 染色体编码 初始种群生成 适应度函数 选择操作 交叉操作 变异操作 5.完整工程文件 1.课题概述 基于GA遗传优化的混…

GZ036 区块链技术应用赛项赛题第4套

2023年全国职业院校技能大赛 高职组 “区块链技术应用” 赛项赛卷&#xff08;4卷&#xff09; 任 务 书 参赛队编号&#xff1a; 背景描述 随着消费需求的不断变化&#xff0c;消费者对于食品安全的关注度越来越高&#xff0c;不仅要求生鲜肉品的肥瘦比例、脂肪…

基于springboot+vue的网上点餐系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 背景和意…

什么是VUE 创建第一个VUE实例

一、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 构建用户界面 的 渐进式 框架 Vue2官网&#xff1a;Vue.js 1.什么是构建用户界面 基于数据渲染出用户可以看到的界面 2.什么是渐进式 所谓渐进式就是循序渐进&#xff0c;不一定非得把Vu…

【GoLang入门教程】Go语言工程结构详述

程序员裁员潮&#xff1a;技术变革下的职业危机 文章目录 程序员裁员潮&#xff1a;技术变革下的职业危机前言总结:专栏集锦强烈推荐写在最后 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网…