【数据结构】顺序表实操——通讯录项目

2024-06-22 21:12

本文主要是介绍【数据结构】顺序表实操——通讯录项目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 前言
  • 1、创建结构体类型
  • 2、通讯录的初始化和销毁
  • 3、通讯录的增删查改
    • 3.1 添加联系人
    • 3.2 删除联系人
    • 3.3 展示联系人
    • 3.4 修改联系人
    • 3.5 查找联系人
  • 4、通讯录菜单
  • 5、通讯录项目代码
  • 总结

前言

本篇文章将介绍一个运用顺序表的例子——通讯录项目。
通讯录我们都知道,细细一想通讯录不就是一个顺序表吗?在通讯录中以一个联系人为单位,存储着若干个联系人的各种信息,我们也可以对通讯录中的联系人信息进行相应的增删查改操作。但是上篇文章的顺序表存的只是整型数据,而在本文联系人的信息可不只是整形数据,如果想把一个联系人的各种信息以一个联系人为单位存储,就需要用到我们之前学到的内置类型——结构体类型。
接下来将详细介绍基于顺序表的通讯录项目搭建的具体过程。
本文将延用上篇文章中实现好的顺序表代码,具体请看—>顺序表


1、创建结构体类型

我们需要存联系人的姓名、性别、年龄、电话、地址等信息,如果把联系人看作一个单位,就需要一个类型能将联系人的这些信息储存,我们很容易的就能想到自定义类型——结构体类型。
所以第一步我们需要创建一个结构体类型来保存联系人的这些信息。

#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100typedef struct personinfo
{char name[NAME_MAX];char gender[GENDER_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}peoinfo;

我们将保存名字等信息的数组大小用宏代替,这样方便后续可能的更改。
创建好用于保存联系人信息的结构体类型后,接着就需要将顺序表头文件中的动态顺序表管理的数据类型替换掉,不要忘了包含相应的头文件。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "contact.h"//顺序表管理数据的类型
//typedef int sl_data_type;
typedef peoinfo sl_data_type;//动态顺序表
typedef struct seqlist
{sl_data_type* arr;int size;//数据个数int capacity;//空间大小
}SL;

2、通讯录的初始化和销毁

我们操作通讯录,实际上就是操作顺序表

那么通讯录的初始化和销毁,实际上就是顺序表的初始化和销毁。
为了方便识别,我们在通讯录头文件中给struct seqlist重新改个名字就叫contact。要想使用顺序表头文件中的struct seqlist原本是需要在通讯录头文件包含顺序表头文件的,但是顺序表头文件中已经包含了通讯录头文件,而我们知道头文件是不能互相包含的,那为了在通讯录头文件中使用struct seqlist,需要在使用前进行前置类型声明。
contact.h:

//给顺序表改一个名字
typedef struct seqlist contact;//前置声明//通讯录的初始化
void contact_init(contact* con);
//通讯录的销毁
void contact_destroy(contact* con);

contact.c:

#include "contact.h"
#include "seqlist.h"//通讯录的初始化
void contact_init(contact* con)
{//通讯录的初始化实际就是顺序表的初始化sl_init(con);
}//通讯录的销毁
void contact_destroy(contact* con)
{sl_destroy(con);
}

3、通讯录的增删查改

3.1 添加联系人

在通讯录中添加联系人实际上就是上篇文章中的在顺序表中插入一个整型数据,只不过在这里把一个联系人当作一个单位。
首先我们需要一个结构体变量来存联系人的各种信息,然后再将这个结构体变量插入到通讯录(顺序表)中,插入方式可以有多种。

//通讯录添加数据
void contact_add(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);//将结构体数据插入到通讯录(顺序表)中sl_push_back(con, info);
}

测试:

void test()
{contact con;// == SL sl;contact_init(&con);contact_add(&con);contact_destroy(&con);
}int main()
{test();return 0;
}

请添加图片描述

可以看到我们成功地添加了一个联系人。


3.2 删除联系人

删除联系人操作有个大前提,就是要删除的这个联系人必须存在,否则无法删除。
怎么判断要删除的这个联系人是否存在呢?
首先我们需要获取要删除的这个联系人的信息,然后在通讯录中查找,如果找到了就可以执行删除操作,如果没找到就不能进行删除操作。
联系人的信息有好几个,我们可以任意选择其中的一个信息来判断是否存在这个联系人。

int find_by_name(contact* con, char name[])
{int i = 0;for (i = 0; i < con->size; i++){if (0 == strcmp(con->arr[i].name, name)){//找到了return i;}}return -1;
}//通讯录删除数据
void contact_delete(contact* con)
{//前提是要删除的这个联系人存在char name[NAME_MAX];printf("请输入您想删除的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("联系人不存在!\n");//直接返回return;}//删除指定位置的数据sl_erase(con, ret);printf("删除成功!\n");
}

上面我们是通过联系人的姓名来判断是否存在这个联系人,姓名是字符串,比较字符串需要用到字符串比较函数strcmp,使用这个函数需要包含头文件<string.h>
如果找到匹配的姓名,则返回这个姓名对应联系人对应的下标,再调用顺序表中删除指定位置数据的函数删除这个联系人。

请添加图片描述


3.3 展示联系人

我们平时使用的通讯录是可以展示联系人的各种信息的,这里我们也简单地实现一下,将所有联系人的信息打印出来供使用者看。

//通讯录数据展示
void contact_show(contact* con)
{//打印表头printf("%2s %8s %8s %8s %8s\n", "姓名", "性别", "年龄", "电话", "地址");printf("———      ———      ———      ———      ———\n");int i = 0;for (i = 0; i < con->size; i++){printf("%2s %7s %7d %9s %9s\n", con->arr[i].name,con->arr[i].gender,con->arr[i].age,con->arr[i].tel,con->arr[i].addr);}
}

在这里插入图片描述


3.4 修改联系人

修改联系人信息也有个大前提,就是要修改的这个联系人必须存在,才能被修改。

//通讯录修改
void contact_modify(contact* con)
{//前提是要修改的联系人存在char name[NAME_MAX];printf("请输入要修改的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("联系人不存在!\n");return;}//修改指定位置的数据printf("请输入新的姓名:\n");scanf("%s", con->arr[ret].name);printf("请输入新的性别:\n");scanf("%s", con->arr[ret].gender);printf("请输入新的年龄:\n");scanf("%d", &con->arr[ret].age);printf("请输入新的电话:\n");scanf("%s", con->arr[ret].tel);printf("请输入新的地址:\n");scanf("%s", con->arr[ret].addr);printf("修改成功!\n");
}

在这里插入图片描述


3.5 查找联系人

我们可以延用之前写好的根据联系人姓名来查找联系人的find_by_name函数。

//通讯录查找
void contact_find(contact* con)
{char name[NAME_MAX];printf("请输入要查找的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("您要查找的联系人不存在!\n");return;}printf("%2s %8s %8s %8s %8s\n", "姓名", "性别", "年龄", "电话", "地址");printf("———      ———      ———      ———      ———\n");printf("%2s %7s %7d %9s %9s\n", con->arr[ret].name,con->arr[ret].gender,con->arr[ret].age,con->arr[ret].tel,con->arr[ret].addr);
}

请添加图片描述


4、通讯录菜单

在实现完通讯录的相关操作后,我们接着制作一个简易菜单。
这个方法我们以前使用过多次了,这里就不再赘述。
其中枚举类型定义在头文件contact.h中。

#include "seqlist.h"void menu()
{printf("*************  通讯录  *************\n");printf("**** 1.添加联系人  2.删除联系人 ****\n");printf("**** 3.修改联系人  4.查找联系人 ****\n");printf("**** 5.展示通讯录  0.退出通讯录 ****\n");printf("************************************\n");
}int main()
{int input = 0;contact con;contact_init(&con);do{menu();printf("请选择您的操作:");scanf("%d", &input);switch (input){case EXIT:printf("退出通讯录!\n");break;case ADD:contact_add(&con);break;case DELETE:contact_delete(&con);break;case MODIFY:contact_modify(&con);break;case FIND:contact_find(&con);break;case SHOW:contact_show(&con);break;default:printf("选择错误,请重新选择!\n");break;}} while (input);//销毁通讯录contact_destroy(&con);return 0;
}

5、通讯录项目代码

seqlist.h:

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "contact.h"//顺序表管理数据的类型
//typedef int sl_data_type;
typedef peoinfo sl_data_type;//动态顺序表
typedef struct seqlist
{sl_data_type* arr;int size;//数据个数int capacity;//空间大小
}SL;//顺序表初始化
void sl_init(SL* ps);
//头插
void sl_push_front(SL* ps, sl_data_type x);
//尾插
void sl_push_back(SL* ps, sl_data_type x);
//在指定位置之前插入数据
void sl_insert(SL* ps, int pos, sl_data_type x);
//头删
void sl_pop_front(SL* ps);
//尾删
void sl_pop_back(SL* ps);
//删除指定位置的数据
void sl_erase(SL* ps, int pos);
//顺序表的查找
int sl_find(SL* ps, sl_data_type x);
//顺序表打印
void sl_print(const SL sl);
//顺序表销毁
void sl_destroy(SL* ps);

seqlist.c:

#define  _CRT_SECURE_NO_WARNINGS#include "seqlist.h"void sl_init(SL* ps)
{ps->arr = NULL;ps->size = 0;ps->capacity = 0;
}//检查是否有空间允许插入数据
void check_capacity(SL* ps)
{if (ps->size == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//为了处理capacity为0的问题sl_data_type* tmp = (sl_data_type*)realloc(ps->arr, newcapacity * sizeof(sl_data_type));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;tmp = NULL;ps->capacity = newcapacity;//及时更新空间大小}
}void sl_push_front(SL* ps, sl_data_type x)
{assert(ps != NULL);check_capacity(ps);int i = 0;for (i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}void sl_push_back(SL* ps, sl_data_type x)
{assert(ps != NULL);check_capacity(ps);ps->arr[ps->size++] = x;
}void sl_insert(SL* ps, int pos, sl_data_type x)
{assert(ps != NULL);assert(pos >= 0 && pos <= ps->size);//确保指定的位置是有效的check_capacity(ps);int i = 0;for (i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}void sl_pop_front(SL* ps)
{assert(ps != NULL);assert(ps->size != 0);//顺序表为空不能删除int i = 0;for (i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}void sl_pop_back(SL* ps)
{assert(ps != NULL);assert(ps->size != 0);//顺序表为空不能删除ps->size--;
}void sl_erase(SL* ps, int pos)
{assert(ps != NULL);assert(ps->size != 0);//实际下面的断言侧面完成了这句代码assert(pos >= 0 && pos < ps->size);//确保指定的位置是有效的int i = 0;for (i = pos; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//int sl_find(SL* ps, sl_data_type x)
//{
//	assert(ps != NULL);
//	int i = 0;
//	for (i = 0; i < ps->size; i++)
//	{
//		if (ps->arr[i] == x)
//		{
//			return i;
//		}
//	}
//	return -1;
//}//void sl_print(const SL sl)
//{
//	int i = 0;
//	for (i = 0; i < sl.size; i++)
//	{
//		printf("%d ", sl.arr[i]);
//	}
//	printf("\n");
//}void sl_destroy(SL* ps)
{assert(ps != NULL);if (ps->arr != NULL)//动态内存函数开辟了空间{free(ps->arr);}ps->arr = NULL;ps->size = 0;ps->capacity = 0;
}

contact.h:

#pragma once#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100enum contact
{EXIT,ADD,DELETE,MODIFY,FIND,SHOW
};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 contact_init(contact* con);
//通讯录的销毁
void contact_destroy(contact* con);
//通讯录添加数据
void contact_add(contact* con);
//通讯录删除数据
void contact_delete(contact* con);
//通讯录修改
void contact_modify(contact* con);
//通讯录查找
void contact_find(contact* con);
//通讯录数据展示
void contact_show(contact* con);

contact.c:

#define  _CRT_SECURE_NO_WARNINGS#include "contact.h"
#include "seqlist.h"
#include <string.h>//通讯录的初始化
void contact_init(contact* con)
{//通讯录的初始化实际就是顺序表的初始化sl_init(con);
}//通讯录的销毁
void contact_destroy(contact* con)
{sl_destroy(con);
}//通讯录添加数据
void contact_add(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);//将结构体数据插入到通讯录(顺序表)中sl_push_back(con, info);printf("联系人添加成功!\n");
}int find_by_name(contact* con, char name[])
{int i = 0;for (i = 0; i < con->size; i++){if (0 == strcmp(con->arr[i].name, name)){//找到了return i;}}return -1;
}//通讯录删除数据
void contact_delete(contact* con)
{//前提是要删除的这个联系人存在char name[NAME_MAX];printf("请输入您想删除的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("联系人不存在!\n");//直接返回return;}//删除指定位置的数据sl_erase(con, ret);printf("删除成功!\n");
}//通讯录数据展示
void contact_show(contact* con)
{//打印表头printf("%2s %8s %8s %8s %8s\n", "姓名", "性别", "年龄", "电话", "地址");printf("———      ———      ———      ———      ———\n");int i = 0;for (i = 0; i < con->size; i++){printf("%2s %7s %7d %9s %9s\n", con->arr[i].name,con->arr[i].gender,con->arr[i].age,con->arr[i].tel,con->arr[i].addr);}
}//通讯录修改
void contact_modify(contact* con)
{//前提是要修改的联系人存在char name[NAME_MAX];printf("请输入要修改的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("联系人不存在!\n");return;}//修改指定位置的数据printf("请输入新的姓名:\n");scanf("%s", con->arr[ret].name);printf("请输入新的性别:\n");scanf("%s", con->arr[ret].gender);printf("请输入新的年龄:\n");scanf("%d", &con->arr[ret].age);printf("请输入新的电话:\n");scanf("%s", con->arr[ret].tel);printf("请输入新的地址:\n");scanf("%s", con->arr[ret].addr);printf("修改成功!\n");
}//通讯录查找
void contact_find(contact* con)
{char name[NAME_MAX];printf("请输入要查找的联系人姓名:\n");scanf("%s", name);int ret = find_by_name(con, name);if (ret < 0){printf("您要查找的联系人不存在!\n");return;}printf("%2s %8s %8s %8s %8s\n", "姓名", "性别", "年龄", "电话", "地址");printf("———      ———      ———      ———      ———\n");printf("%2s %7s %7d %9s %9s\n", con->arr[ret].name,con->arr[ret].gender,con->arr[ret].age,con->arr[ret].tel,con->arr[ret].addr);
}

test.c:

#define  _CRT_SECURE_NO_WARNINGS#include "seqlist.h"void menu()
{printf("*************  通讯录  *************\n");printf("**** 1.添加联系人  2.删除联系人 ****\n");printf("**** 3.修改联系人  4.查找联系人 ****\n");printf("**** 5.展示通讯录  0.退出通讯录 ****\n");printf("************************************\n");
}int main()
{int input = 0;contact con;contact_init(&con);do{menu();printf("请选择您的操作:");scanf("%d", &input);switch (input){case EXIT:printf("退出通讯录!\n");break;case ADD:contact_add(&con);break;case DELETE:contact_delete(&con);break;case MODIFY:contact_modify(&con);break;case FIND:contact_find(&con);break;case SHOW:contact_show(&con);break;default:printf("选择错误,请重新选择!\n");break;}} while (input);//销毁通讯录contact_destroy(&con);return 0;
}

总结

  • 通讯录其实就是顺序表,只是上文的顺序表存储的是整型数据,而本文的顺序表存储的结构体类型数据而已
  • 在本篇文章中我们更多的是对保存联系人信息的结构体类型进行相应的操作,通讯录实际上就是顺序表,而顺序表的相关操作是我们已经实现好了的,所以我们只需要拿来用就可以了
  • 通讯录就像是在顺序表的外面又包装了一层其他的操作,使其完成对联系人信息的增删查改操作

这篇关于【数据结构】顺序表实操——通讯录项目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085428

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

关于Spring @Bean 相同加载顺序不同结果不同的问题记录

《关于Spring@Bean相同加载顺序不同结果不同的问题记录》本文主要探讨了在Spring5.1.3.RELEASE版本下,当有两个全注解类定义相同类型的Bean时,由于加载顺序不同,最终生成的... 目录问题说明测试输出1测试输出2@Bean注解的BeanDefiChina编程nition加入时机总结问题说明

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

配置springboot项目动静分离打包分离lib方式

《配置springboot项目动静分离打包分离lib方式》本文介绍了如何将SpringBoot工程中的静态资源和配置文件分离出来,以减少jar包大小,方便修改配置文件,通过在jar包同级目录创建co... 目录前言1、分离配置文件原理2、pom文件配置3、使用package命令打包4、总结前言默认情况下,

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

python实现简易SSL的项目实践

《python实现简易SSL的项目实践》本文主要介绍了python实现简易SSL的项目实践,包括CA.py、server.py和client.py三个模块,文中通过示例代码介绍的非常详细,对大家的学习... 目录运行环境运行前准备程序实现与流程说明运行截图代码CA.pyclient.pyserver.py参

IDEA运行spring项目时,控制台未出现的解决方案

《IDEA运行spring项目时,控制台未出现的解决方案》文章总结了在使用IDEA运行代码时,控制台未出现的问题和解决方案,问题可能是由于点击图标或重启IDEA后控制台仍未显示,解决方案提供了解决方法... 目录问题分析解决方案总结问题js使用IDEA,点击运行按钮,运行结束,但控制台未出现http://

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时