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

news2024/11/15 11:25:15

前言

昨天我们踏入了数据结构的深山,并且和顺序表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.coloradmin.cn/o/1403433.html

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

相关文章

二维码地址门牌管理系统:预约安全、智能生活

文章目录 前言一、访客预约功能二、安全性保障三、智慧小区生活 前言 二维码地址门牌管理系统的出现不仅提升了小区的安全性&#xff0c;还为访客提供了更便捷的预约服务&#xff0c;让亲朋好友轻松进入小区。 一、访客预约功能 该系统提供了访客预约功能&#xff0c;业主可为…

【GitHub项目推荐--12306 抢票助手 python】【转载】

这个项目名很干脆&#xff0c;不知道以为是 12306 网站的源码&#xff0c;其实不是这是全 GitHub最德高望重的抢票小助手&#xff0c;功能一直在更新&#xff0c;且现已支持 Python 3.6 以上版本。 开源地址&#xff1a;https://github.com/testerSunshine/12306

安装向量数据库milvus可视化工具attu

使用docker安装的命令和简单就一个命令&#xff1a; docker run -p 8000:3000 -e MILVUS_URL{milvus server IP}:19530 zilliz/attu:v2.3.5sunyuhuasunyuhua-HKF-WXX:~/dockercom/milvus$ docker run -p 8000:3000 -e MILVUS_URL127.0.0.1:19530 zilliz/attu:latest yarn run…

代码随想录 Leetcode1047. 删除字符串中的所有相邻重复项

题目&#xff1a; 代码(首刷自解 2024年1月21日&#xff09;&#xff1a; class Solution { public:string removeDuplicates(string s) {if (s.size() < 2) return s;stack<char> t;for (int i 0; i < s.size(); i) {if (t.empty()) t.push(s[i]);else {if (s[i…

IoC 容器总结

目录 理解 IoC 实现方式 DI 实现原理 Autowired VS Resource 区别 IoC 和 DI 有什么区别 理解 IoC IoC——控制反转&#xff0c;是 Spring 框架的核心概念之一&#xff0c;是一种设计原则和编程模式&#xff0c;用于实现松耦合和可测试的应用程序 控制反转&#xff1a;对…

《二叉树》——1

目录 前言&#xff1a; 二叉树的链式结构 二叉树的遍历 前序遍历&#xff1a; 中序遍历&#xff1a; 后序遍历&#xff1a; 总结&#xff1a; 前言&#xff1a; 从本文开始&#xff0c;将进一步深入学习编程语言思想&#xff0c;从二叉树开始我们将接触许许多多的递归算…

力扣hot100 合并两个有序链表 递归 双指针

Problem: 21. 合并两个有序链表 文章目录 &#x1f496; 递归思路 &#x1f496; 双指针 &#x1f496; 递归 思路 &#x1f468;‍&#x1f3eb; 参考地址 n , m n,m n,m 分别为 list1 和 list2 的元素个数 ⏰ 时间复杂度: O ( n m ) O(nm) O(nm) &#x1f30e; 空间复杂…

力扣62. 不同路径

动态规划 思路&#xff1a; 定义 dp[r][c] 为到达坐标 (r, c) 的路径数&#xff1a; 它只能有同一行左边相邻方格向右到达或者同一列上方相邻方格向下到达&#xff1b;状态转移方程&#xff1a; dp[r][c] dp[r][c - 1] dp[r - 1][c]初始状态 dp[0][0] 1第一行的路径数是 1第…

恒创科技:云服务器配置中的vCPU与物理CPU有啥区别?

​  说到云服务器&#xff0c;您可能经常会遇到vCPU这个词&#xff0c;而且它和物理CPU经常被拿来谈论。尽管它们听起来相似&#xff0c;但两者之间存在显著差异。在本文中&#xff0c;我们将详细讨论云vCPU和物理CPU之间的差异。 物理与虚拟 CPU 和 vCPU 之间最显著的区别在…

【Linux】-对于信号章节补充的知识点,以及多线程知识的汇总

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

非线性最小二乘问题的数值方法 —— 狗腿法 Powell‘s Dog Leg Method (II, Python 简单实例)

Title: 非线性最小二乘问题的数值方法 —— 狗腿法 Powell‘s Dog Leg Method (II, Python 简单实例) 姊妹博文 非线性最小二乘问题的数值方法 —— 狗腿法 Powell‘s Dog Leg Method (I - 原理与算法) 0.前言 本篇博文作为对前述 “非线性最小二乘问题的数值方法 —— 狗腿法…

L1-069 胎压监测(Java)

小轿车中有一个系统随时监测四个车轮的胎压&#xff0c;如果四轮胎压不是很平衡&#xff0c;则可能对行车造成严重的影响。 让我们把四个车轮 —— 左前轮、右前轮、右后轮、左后轮 —— 顺次编号为 1、2、3、4。本题就请你编写一个监测程序&#xff0c;随时监测四轮的胎压&…

系统架构14 - 软件工程(2)

需求工程 需求工程软件需求两大过程三个层次业务需求(business requirement)用户需求(user requirement)功能需求 (functional requirement)非功能需求 概述活动阶段需求获取基本步骤获取方法 需求分析三大模型数据流图数据字典DD需求定义方法 需求验证需求管理需求基线变更控制…

Hylicos - MINI2440 - 中断控制

中断 中断源管理 中断是一种异步异常&#xff0c;CPU需要处理很多来自设备的中断请求&#xff0c;而CPU引出的line只有IRQ线和FIQ线&#xff0c;所以就得引入中断控制器帮助CPU搞清楚是中断的来源。 MINI2440的中断控制器&#xff0c;可以接受来自60个中断源的请求。提供这些…

Redis双写一致性

所有的情况都是再并发情况下存在温蒂 一、先更新数据库&#xff0c;再更新缓存场景-不推荐 当有两个线程A、B&#xff0c;同时对一条数据进行操作&#xff0c;一开始数据库和redis的数据都为1&#xff0c;当线程A去修改数据库&#xff0c;将1改为2&#xff0c;然后线程A在修改…

幻兽帕鲁专用服务器

随着幻兽帕鲁这款游戏的热度持续升温&#xff0c;我们遍寻全网&#xff0c;带给各位玩家一个全新的、高品质的游戏体验——莱卡云服务器。有幻兽帕鲁的热衷者们无需再为了服务器的选取困扰&#xff0c;因为我们可以肯定地说&#xff1a;选择莱卡云&#xff0c;你不会失望。 首先…

数据库复试—关系数据库标准语言SQL

数据库复试—关系数据库标准语言SQL SQL&#xff1a;结构化查询语言 以教材中的学生-课程数据库为例进行SQL基础语法的复习 数据库实验环境选择SQLServer 11 关系模式 学生表Student(Sno,Sname,Ssex,Sage,Sdept) 课程表Course(Cno,Cname,Cpno,Ccredit) 学生选课表SC&#xf…

Linux操作系统——理解软硬链接

1.引言 通过我们前面理解文件系统的基础上&#xff0c;我们来理解软硬链接&#xff0c;在我们学习文件的时候我们会见到各种链接文件&#xff0c;其中链接文件分为两种&#xff0c;一种叫软连接&#xff0c;一种叫硬链接。 下面我们新建一个log文件&#xff0c;要对log这个文…

搭建一个简单的Spring Demo

要学习Spring 源码&#xff0c;一个是从Spring GitHub 上去down源码&#xff0c;然后倒入IDEA编译&#xff0c;但这种方法费时费力&#xff0c;如果你不需要对Spring 源码进行修改后&#xff0c;再编译的话&#xff0c;直接搭建一个Spring Demo 的Maven项目&#xff0c;引入Spr…