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

前言

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

#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

#include

//前置声明

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

🐻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 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 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 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 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 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题来巩固一下。


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

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

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/d4f600db73.html