锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 【数据结构】利用顺序表实现通讯录

【数据结构】利用顺序表实现通讯录

0
  • 软件开发
  • 发布于 2024-09-29
  • 13 次阅读
黄健
黄健


文章目录

  • 前言
  • 通讯录要求
  • 利用顺序表的现有功能
  • 代码呈现



前言

这篇文章实现的通讯录利用了笔者上一篇写的有关顺序表的应用 https://blog.csdn.net/2301_77954967/article/details/137360029?spm=1001.2014.3001.5502,需要用的朋友自行复制

通讯录要求

1)至少能够存储100个人的通讯信息
2)能够保存用户信息:名字、性别、年龄、电话、地址等
3)增加联系人信息
4)删除指定联系人
5)查找制定联系人
6)修改指定联系人
7)显示联系人信息
8)能够保存信息用于下次读取


利用顺序表的现有功能

我们现在contact.h的头文件里建立如下结构体,

#define NAME_MAX 100
#define GENDER_MAX 10  //male female
#define TEL_MAX 20
#define ADDR_MAX 100

//定义联系人数据结构
//姓名 性别 年龄 电话 地址
typedef struct personInfo
{

    char name[NAME_MAX];
    char gender[GENDER_MAX];
    int age;
    char tel[TEL_MAX];
    char addr[ADDR_MAX];
}peoInfo;

我们可以把这个通讯录中的每个人的信息看成一个顺序表中每一个数组的数据,这样一来就只需要将这里的类型名 peoInfo 给重命名成 SLDataType 就行了,需要注意的是,这里是两个头文件,不仅需要放在同一目录 下,还需要在 SeqList.h 文件里附加上 Contact.h 的头文件名

#include"Contact.h"

typedef peoInfo SLDataType;//将proInfo改成SLDataType

typedef struct SeqList
{

    SLDataType* arr;
    int size;//有效数据个数
    int capacity;//空间大小
}SL;

再然后,如果后续的功能名仍用 SL 来命名会有些模糊,但是两个头文件之间不能互相引用 ,那么这里我们可以回到 Contact.h 里,进行前置声明,

typedef struct SeqList Contact;//前置声明,用顺序表里第一个创立的结构体名SeqList,而不是SL

这样一来就是要进行方案功能的实现就行了,开始工作做好了,后面就会简单很多

//初始化通讯录
void InitContact(Contact* con);

//添加通讯录数据
void AddContact(Contact* con);

//删除通讯录数据
void DelContact(Contact* con);

//展示通讯录数据
void ShowContact(Contact* con);

//查找通讯录数据
void FindContact(Contact* con);

//修改通讯录数据
void ModifyContact(Contact* con);

//销毁通讯录数据
void DestroyContact(Contact* con);

假设我们上面几个方法都已经完善并测验好了,我们需要思考的如何使这些数据进行保存,方便下一次读取,这里就需要用到文件了

这里我们选择用二进制文件的方式读取和写入,原因是,这里我们利用了 peoInfo 这个现有的结构体,和 SLPushBack 这个现有的方法,不需要以文本文件格式化的形式读入文件,再花费功夫去调整文本文件的格式


代码呈现

Contact.h

#pragma once//同一个文件不会被包含多次。这里所说的"同一个文件"是指物理上的一个文件,而不是指内容相同的两个文件

#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>




#define NAME_MAX 100
#define GENDER_MAX 10  //male female
#define TEL_MAX 20
#define ADDR_MAX 100



//定义联系人数据结构
//姓名 性别 年龄 电话 地址
typedef struct personInfo
{

    char name[NAME_MAX];
    char gender[GENDER_MAX];
    int age;
    char tel[TEL_MAX];
    char addr[ADDR_MAX];
}peoInfo;


typedef struct SeqList Contact;//前置声明,用到顺序表里第一个创立的结构体名
//这里的通讯录实际上就是顺序表

//要用到顺序表相关的方法,对通讯录的操作实际就是对顺序表进行操作
// 
//通讯录相关的方法

//初始化通讯录
void InitContact(Contact* con);

//添加通讯录数据
void AddContact(Contact* con);

//删除通讯录数据
void DelContact(Contact* con);

//展示通讯录数据
void ShowContact(Contact* con);

//查找通讯录数据
void FindContact(Contact* con);

//修改通讯录数据
void ModifyContact(Contact* con);

//销毁通讯录数据
void DestroyContact(Contact* con);

//利用文件保存数据
//读取
void LoadContact(Contact* con);

//写入
void SaveContact(Contact* con);

SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


#include"Contact.h"//头文件之间不能互相包含



typedef peoInfo SLDataType;//将proInfo改成SLDataType

typedef struct SeqList
{

    SLDataType* arr;
    int size;//有效数据个数
    int capacity;//空间大小
}SL;


//typedef struct SeqList SL;//将这个顺序表的类型名简化成SL


//顺序表初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestory(SL* ps);

//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部插入
void SLPushFront(SL* ps, SLDataType x);

//尾部删除
void SLPopBack(SL* ps);
//头部删除
void SLPopFront(SL* ps);


//扩容
void SLCheckCapacity(SL* ps);
//打印
void SLPrint(SL s);

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//删除指定位置的数据
void SLErase(SL* ps, int pos);

//查找
int SLFind(SL* ps, SLDataType x);

Contact.c

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


//初始化通讯录
void InitContact(Contact* con)
{

    //实际上要进行的是顺序表的初始化
    //con->arr = NULL;
    //con->capacity = con->size = 0;
    SLInit(con);
    LoadContact(con);
}


//销毁通讯录数据
void DestroyContact(Contact* con)
{

    SaveContact(con);
    SLDestory(con);
}


//添加通讯录数据
void AddContact(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);//顺序表中已有功能的复用
}


//查找通讯录数据(依靠名字)
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;
}


//通讯录删除数据
void DelContact(Contact* con)
{

    //要删除的数据必须要存在,才能执行删除操作
    //查找
    char name[NAME_MAX];
    printf("请输入要删除的联系人名字:\n");
    scanf("%s", name);

    int find = FindByName(con, name);
    if (find < 0)
    {

        printf("要删除的联系人数据不存在!\n");
        return;
    }
    //要删除的联系人数据存在从 -> 知道了要删除联系人的下标
    SLErase(con, find);
    printf("删除成功!\n");
}


//展示通讯录数据
void ShowContact(Contact* con)
{

    //打印表头
    printf("%5s %5s %5s %5s %5s\n", "姓名", "性别", "年龄", "电话", "地址");
    //遍历通讯录,按照格式打印每个联系人数据
    for (int i = 0; i < con->size; i++)
    {

        printf("%5s %5s %5d %5s %5s\n",
            con->arr[i].name,
            con->arr[i].gender,
            con->arr[i].age,
            con->arr[i].tel,
            con->arr[i].addr
            );
    }
}

//修改通讯录数据
void ModifyContact(Contact* con)
{

    char name[NAME_MAX];
    printf("请输入要修改的数据\n");
    scanf("%s", name);
    int find = FindByName(con,name);
    if (find < 0)
    {

        printf("要修改的联系人数据不存在\n");
        return;
    }
    //直接修改
    printf("请输入新的姓名:\n");
    scanf("%s",con->arr[find].name);
    printf("请输入新的性别:\n");
    scanf("%s",con->arr[find].gender);
    printf("请输入新的年龄:\n");
    scanf("%d",&con->arr[find].age);
    printf("请输入新的电话:\n");
    scanf("%s",con->arr[find].tel);
    printf("请输入新的地址:\n");
    scanf("%s",con->arr[find].addr);

    printf("修改成功!\n");
}

//查找通讯录数据
void FindContact(Contact* con)
{

    char name[NAME_MAX];
    printf("请输入要查找的联系人:\n");
    scanf("%s", name);
    int find = FindByName(con,name);
    if (find < 0)
    {

        printf("要查找的联系人数据不存在\n");
        return;
    }
    printf("%5s %5s %5s %5s %5s\n", "姓名", "性别", "年龄", "电话", "地址");
    printf("%5s %5s %5d %5s %5s\n",
        con->arr[find].name,
        con->arr[find].gender,
        con->arr[find].age,
        con->arr[find].tel,
        con->arr[find].addr
    );

}


//利用文件保存数据
//读取
void LoadContact(Contact* con)
{

    FILE* pf = fopen("contact.txt", "rb");
    if (pf == NULL)
    {

        perror("fopen error!\n");
        return;
    }

    //循环读取文件数据
    peoInfo info;
    while (fread(&info, sizeof(peoInfo), 1, pf))
    {

        SLPushBack(con, info);
    }

    fclose(pf);
    pf = NULL;

    printf("历史数据写入成功!\n");
}


//写入
void SaveContact(Contact* con)
{

    FILE* pf = fopen("contact.txt", "wb");
    if (pf == NULL)
    {

        perror("fopen error!\n");
        return;
    }
    for (int i = 0; i < con->size; i++)
    {

        fwrite(con->arr + i, sizeof(peoInfo), 1, pf);
    }

    fclose(pf);
    pf = NULL;


    printf("通讯录数据保存成功!\n");
}

SeqList.c

#include"SeqList.h"

//初始化顺序表
void SLInit(SL* ps)
{

    ps->arr = NULL;
    ps->size = ps->capacity = 0;

}

//顺序表的销毁
void SLDestory(SL* ps)
{

    if (ps->arr)
    {

        free(ps->arr);
    }
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}

//扩容
void SLCheckCapacity(SL* ps)
{

    //插入数据前看看空间够不够
    if (ps->size == ps->capacity)
    {

        //扩容,申请空间
        int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//三目表达式
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * 2 * sizeof(SLDataType));
        if (tmp == NULL)
        {

            perror("realloc fail!");
            exit(1);
        }
        //空间申请成功
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}

//打印
void SLPrint(SL s)
{

    for (int i = 0; i < s.size; i++)
    {

        printf("%d ", s.arr[i]);
    }
    printf("\n");
}


//在尾部插入
void SLPushBack(SL* ps, SLDataType x)
{

    //温柔的解决方式
    //if(ps == NULL)
    //{

    //  return 0;
    //}
    assert(ps);//

    //扩容
    SLCheckCapacity(ps);

    //ps->arr[ps->size] = x;
    //++ps->size;
    ps->arr[ps->size++] = x;
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{

    //扩容
    SLCheckCapacity(ps);

    //先将顺序表中已有的数据整体往后移动一位
    for (int i = ps->size; i > 0; i--)
    {

        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[0] = x;
    ps->size++;
}

//尾部删除
void SLPopBack(SL* ps)
{

    assert(ps);
    assert(ps->size);
    //顺序表不为空
    --ps->size;
}

//头部删除
void SLPopFront(SL* ps)
{

    assert(ps);
    assert(ps->size);
    //顺序表不为空
    //数据整体王前移动一位
    for (int i = 0; i < ps->size - 1; i++)
    {

        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{

    assert(ps);
    assert(pos >= 0 && pos <= ps->size);

    //扩容,判断空间够不够
    SLCheckCapacity(ps);
    //让pos之后及其自身往后移动一位
    for (int i = ps->size; i > pos; i--)
    {

        ps->arr[i] = ps->arr[i - 1];
    }
    ps->size++;
    ps->arr[pos] = x;
}

//删除指定位置的数据
void SLErase(SL* ps, int pos)
{

    assert(ps);
    assert(pos >= 0 && pos < ps->size);
    for (int i = pos; i < ps->size - 1; i++)
    {

        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

//查找
//int SLFind(SL* ps, SLDataType x)
//{

//  assert(ps);
//  for (int i = 0; i < ps->size; i++)
//  {

//      if (ps->arr[i] == x)
//      {

//          return i;
//      }
//  }
//  return -1;
//}

test.c

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


通讯录测试
//void ContactTest01()
//{

//  Contact con;//创建通讯录对象
//  InitContact(&con);
//
//  //测试添加数据功能
//  AddContact(&con);
//  AddContact(&con);
//
//  //测试删除数据功能
//  DelContact(&con);
//
//  //测试打印功能
//  ShowContact(&con);
//  
//  //销毁通讯录数据
//  DestroyContact(&con);
//
//}
//
//int main1()
//{

//  ContactTest01();
//  return 0;
//}

void menu()
{

    printf("*******************通讯录******************\n");
    printf("*****1.增加联系人  ******  2.删除联系人****\n");
    printf("*****3.修改联系人  ******  4.查找联系人****\n");
    printf("*****5.展示联系人  ******  0.  退  出  ****\n");
    printf("*******************************************\n");
}
int main()
{

    int op = -1;
    Contact con;
    InitContact(&con);

    do
    {

        menu();
        printf("请选择您的操作:\n");
        scanf("%d", &op);

        //要根据对应的op执行不同的操作
        switch (op)
        {

        case 1:
            AddContact(&con);
            break;
        case 2:
            DelContact(&con);
            break;
        case 3:
            ModifyContact(&con);
            break;
        case 4:
            FindContact(&con);
            break;
        case 5:
            ShowContact(&con);
            break;
        case 0:
            op = 0;
            printf("退出通讯录中......\n");
            break;
        default:
            printf("选择错误!请重新选择\n");
            break;

        }
    }while(op != 0);
    printf("退出成功\n");


    DestroyContact(&con);

    return 0;
}

原文链接: https://blog.csdn.net/2301_77954967/article/details/137407676

标签: #软件开发 1171
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.