【数据结构】4.单链表实现通讯录

news2025/5/25 0:24:09

在上一篇文章我们学会了用单链表来实现各种方法,在这一篇文章我们将在单链表的基础上实现通讯录。

0、准备工作

实现通讯录之前,我们还需要在单链表的基础上添加2个文件,头文件Contact.h和源文件Contact.c。Contact.c来实现通讯录方法的声明,而Contact.h来实现通讯录的具体方法。
通讯录的数据其实就是存储在单链表上的,只不过变成了一个结构体,因此我们需要在Contact.h定义一个联系人数据的结构体。
如下图所示:
在这里插入图片描述
代码如下:

typedef struct PersonInfo
{
	char name[NAME_MAX];
	char gender[GENDER_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

为了代码有更好的延展性,我们还可以对数组的内存大小进行宏定义,在Contact.h的头部定义:

#define NAME_MAX 10
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100

由于我们是再单链表的基础上进行的,所以我们可以对单链表重新起个名字叫做通讯录,在Contact.h中定义:typedef struct SListNode contact;

由于我们是将类型由整型类型改为结构体类型,那么我们还需要再SList,h中将类型重定义一下:typedef struct PersonInfo SLDataType;

接着就可以在Contact.h中声明一系列的方法:

//通讯录的初始化
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);

接下来再在Contact.c进行方法的实现,在实现之前我们需要包含以下头文件:

#include"Contact.h"
#include"SList.h"

1、初始化通讯录

初始化代码:

void InitContact(contact** con)
{
	LoadContact(con);
}

通讯录的初始化实际上就是将文件的数据导入到通讯录,确保通讯录一开始就拥有之前的数据,而不是一个空壳。
将文件数据导入到通讯录代码如下:

void LoadContact(contact** con)
{
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen fail!");
		return;
	}
	//定义通讯录变量
	PeoInfo info;
	//依次读取文件数据
	while (fread(&info, sizeof(info), 1, pf))
	{
		//将文件数据尾插到通讯录中
		SLTPushBack(con, info);
	}
	printf("历史通讯录数据导入成功!\n");

	//有开有闭
	fclose(pf);
}

2、添加通讯录数据

思路:创建通讯录局部变量,输入对应信息后,再尾插到通讯录中。

void AddContact(contact** con)
{
	PeoInfo info;
	printf("请输入要添加的联系人姓名:");
	scanf("%s", info.name);
	printf("请输入要添加的联系人性别:");
	scanf("%s", info.gender);
	printf("请输入要添加的联系人年龄:");
	scanf("%d", &info.age);
	printf("请输入要添加的联系人电话:");
	scanf("%s", info.tel);
	printf("请输入要添加的联系人地址:");
	scanf("%s", info.addr);

	SLTPushBack(con, info);
	printf("添加联系人成功!\n");
}

再进行测试:

int main()
{
	contact* con=NULL;

	AddContact(&con);
	return 0;
}

运行结果:

我们再进行调试观察是否真正的插入成功:

可以观察到确实成功插入数据了!

3、展示通讯录数据

为了方便我们观察方法是否成功进行,我们可以先编写展示通讯录数据的方法。

思路:先打印表头,再创建指针cur指向头结点,遍历整个通讯录,依次打印对应的信息。

void ShowContact(contact* con)
{
	printf("%-10s %-4s %-4s %-11s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	contact* cur = con;
	while (cur)
	{
		printf("%-10s %-4s %4s %-11s %-20s\n", 
			cur->data.name,
			cur->data.gender,
			cur->data.age,
			cur->data.tel,
			cur->data.addr
		);
		cur = cur->next;
	}
}

再进行测试:

int main()
{
	contact* con=NULL;
	AddContact(&con);

	ShowContact(con);
	return 0;
}

通讯录数据显示成功!

4、删除通讯录数据

在进行删除,修改等一系列操作的时候,我们首先需要知道删除或修改的对象是谁,因此还需要一个函数专门根据姓名来查找到联系人,再进行后续的一系列操作。

查找思路:传一个头结点,和要查找的姓名,先创建指针指向头结点,再遍历通讯录,看要查找的名字是否在通讯录中,在就返回下标,不在就返回空。

contact* FindByName(contact* con, char name[])
{
	contact* cur = con;
	while (cur)
	{
		if (0 == strcmp(name, cur->data.name))
		{
			return cur;
		}
		cur = cur->next;
	}
	//没找到返回NULL
	return NULL;
}

接着就可以删除通讯录数据了。
思路:输入要查找的姓名,调用查找函数找到下标,如果下标为空就表示没有找到,并且终止程序,下标不为空就执行删除指定位置数据的操作。

void DelContact(contact** con)
{
	char name[NAME_MAX];
	printf("请输入要删除的联系人姓名:");
	scanf("%s", name);

	contact* pos = FindByName(*con, name);
	if (pos == NULL)
	{
		printf("你要删除的联系人不存在!\n");
		return;
	}
	SLTErase(con, pos);
	printf("删除成功!\n");
}

再进行测试:

int main()
{
	contact* con=NULL;
	AddContact(&con);
	AddContact(&con);
	ShowContact(con);
	
	DelContact(&con);
	ShowContact(con);
	return 0;
}

运行结果:

我们可以观察到删除成功!

5、查找通讯录数据

思路:输入要查找的姓名,调用查找函数找到下标,下标为空就没找到并终止程序,不为空就直接展示下标所对应的数据。

void FindContact(contact** con)
{
	char name[NAME_MAX];
	printf("请输入要查找的联系人姓名:");
	scanf("%s", name);

	contact* pos = FindByName(*con, name);
	if (pos == NULL)
	{
		printf("你要查找的联系人不存在!\n");
		return;
	}
	printf("查找成功!\n");
	//打印下标对应的数据
	printf("%-10s %-4s %-4s %-11s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%-10s %-4s %-4d %-11s %-20s\n",
		pos->data.name,
		pos->data.gender,
		pos->data.age,
		pos->data.tel,
		pos->data.addr
	);
}

再进行测试:

int main()
{
	contact* con=NULL;
	AddContact(&con);
	AddContact(&con);

	FindContact(&con);
	return 0;
}

运行结果:

6、修改通讯录数据

思路:输入要查找的姓名,调用查找函数找到下标,下标为空就没找到并终止程序,不为空就直接修改下标所对应的数据。

void ModifyContact(contact** con)
{
	char name[NAME_MAX];
	printf("请输入要修改的联系人姓名:");
	scanf("%s", name);

	contact* pos = FindByName(*con, name);
	if (pos == NULL)
	{
		printf("你要修改的联系人不存在!\n");
		return;
	}

	//修改下标对应的数据
	printf("请输入要修改的姓名:");
	scanf("%s", pos->data.name);
	printf("请输入要修改的性别:");
	scanf("%s", pos->data.gender);
	printf("请输入要修改的年龄:");
	scanf("%d", &pos->data.age);
	printf("请输入要修改的电话:");
	scanf("%s", pos->data.tel);
	printf("请输入要修改的地址:");
	scanf("%s", pos->data.addr);

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

再进行测试:

int main()
{
	contact* con=NULL;
	AddContact(&con);
	ShowContact(con);

	ModifyContact(&con);
	ShowContact(con);
	return 0;
}

运行结果:

修改成功!

7、保存通讯录数据

在我们实现以上方法后,我们输入数据之后可以将其保存,下一次再直接导入使用,因此还需要写一个保存数据的函数。
思路:打开文件,创建指针cur指向头结点,遍历单链表,依次将cur对应的联系人数据写入文件中。

void SaveContact(contact* con)
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen fail!");
		return;
	}
	contact* cur = con;
	while (cur)
	{
		//将当前节点对应的联系人数据写入文件
		fwrite(&(cur->data), sizeof(PeoInfo), 1, pf);
		cur = cur->next;
	}
	printf("保存成功!\n");
	fclose(pf);
}

我们再进行测试:

int main()
{
	contact* con=NULL;
	AddContact(&con);
	
	SaveContact(con);
	return 0;
}

可以观察到,数据已经成功被保存到文件中了!

8、销毁通讯录

思路:先保存通讯录数据,再调用单链表的销毁函数。

void DestroyContact(contact** con)
{
	SaveContact(*con);
	SListDestroy(con);
}

再进行测试:

int main()
{
	contact* con = NULL;
	AddContact(&con);

	DestroyContact(&con);

	return 0;
}

在这里插入图片描述
可以观察到数据已经成功销毁了,同时数据已经被存储到文件中了。

9、通讯录的完整实现

再实现了上述方法之后,我们就可以在test.c中编写可视化的操作页面,通过调用通讯录的方法来完整实现通讯录了。
思路:先编写主界面函数,主函数:输入值先赋值为-1,通讯录初始化,再使用do while循环条件输入0终止,调用主界面,输入值,根据输入的值使用switch来执行对应的操作,最后再将通讯录销毁。

void menu()
{
	printf("******************     通讯录     ******************\n");
	printf("*********  1、增加联系人    2、删除联系人  **************\n");
	printf("*********  3、查找联系人    4、修改联系人  **************\n");
	printf("*********  5、展示联系人    6、保存联系人  **************\n");
	printf("*********          0、退出通讯录           **************\n");
}

int main()
{
	int input = -1;
	contact* con = NULL;
	InitContact(&con);

	do
	{
		menu();
		printf("请选择你的操作:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			FindContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(con);
			break;
		case 6:
			SaveContact(con);
			break;
		case 0:
			printf("退出通讯录...\n");
			break;
		default:
			printf("输入错误,请重新选择你的操作:\n");
			break;
		}
	} while (input!=0);

	DestroyContact(&con);
	return 0;
}

再进行测试:

发现已经可以实现完整的通讯录功能了。

点击在gitee查看完整源代码

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

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

相关文章

接口自动化测试(一)

一、HTTP请求的核心概念及原理详解 HTML:超文本标记语言-----通过<标记符>内容</标记符>格式-------页面 URL:统一资源定位符 返回数据有很多&#xff1a;页面、图片、视频&#xff0c;都可以进行返回---统称为&#xff1a;资源HTTP:超文本传输协议(请求-响应的协…

【JavaEE】Spring AOP的注解实现

目录 一、AOP 与 Spring AOP二、Spring AOP简单实现三、详解Spring AOP3.1 Spring AOP 核心概念3.1.1 切点&#xff08;Pointcut&#xff09;3.1.2 连接点&#xff08;Join Point&#xff09;3.1.3 通知&#xff08;Advice&#xff09;3.1.4 切面&#xff08;Aspect&#xff09…

揭秘大数据 | 22、软件定义存储

揭秘大数据 | 19、软件定义的世界-CSDN博客 揭秘大数据 | 20、软件定义数据中心-CSDN博客 揭秘大数据 | 21、软件定义计算-CSDN博客 老规矩&#xff0c;先把这个小系列的前三篇奉上。今天书接上文&#xff0c;接着叙软件定义存储的那些事儿。 软件定义存储源于VMware公司于…

OpenCV 图形API(37)图像滤波-----分离过滤器函数sepFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 应用一个可分离的线性滤波器到一个矩阵&#xff08;图像&#xff09;。 该函数对矩阵应用一个可分离的线性滤波器。也就是说&#xff0c;首先&a…

flutter下载SDK环境配置步骤详解

目录 1.Flutter官网地址、SDK下载地址? 1.1 选择你电脑的系统​ 2.配置环境 3.解决环境报错 zsh:command not found:flutter 1.Flutter官网地址、SDK下载地址? flutter官网地址: URL 1.1 选择你电脑的系统 下载解压动目录就OK了 2.配置环境 1、打开命令行&#xf…

数据结构与算法入门 Day 0:程序世界的基石与密码

&#x1f31f;数据结构与算法入门 Day 0&#xff1a;程序世界的基石与密码&#x1f511; ps&#xff1a;接受到了不少的私信反馈&#xff0c;说应该先把前置的知识内容做一个梳理&#xff0c;所以把昨天的文章删除了&#xff0c;重新开启今天的博文写作 Hey 小伙伴们&#xff…

vscode终端运行windows服务器的conda出错

远程windows服务器可以运行&#xff0c;本地vscode不能。 打开vscode settings.json文件 添加conda所在路径

“大湾区珠宝艺境花园”璀璨绽放第五届消博会

2025年4月13日&#xff0c;第五届中国国际消费品博览会&#xff08;以下简称"消博会"&#xff09;重要主题活动——《大湾区珠宝艺境花园》启动仪式在海南国际会展中心2号馆隆重举行。由广东省金银珠宝玉器业厂商会组织带领粤港澳大湾区优秀珠宝品牌&#xff0c;以“…

十、自动化函数+实战

Maven环境配置 1.设计测试用例 2.创建空项目 1&#xff09;添加需要的依赖pom.xml <dependencies> <!-- 截图配置--><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</…

Day09【基于jieba分词和RNN实现的简单中文分词】

基于jieba分词和RNN实现的中文分词 目标数据准备主程序预测效果 目标 本文基于给定的中文词表&#xff0c;将输入的文本基于jieba分词分割为若干个词&#xff0c;词的末尾对应的标签为1&#xff0c;中间部分对应的标签为0&#xff0c;同时将分词后的单词基于中文词表做初步序列…

自动化测试——selenium

简介 Selenium 是一个广泛使用的自动化测试工具&#xff0c;主要用于 Web 应用程序的自动化测试。它能实现的功能是网页的自动化操作&#xff0c;例如自动抢票刷课等。同时你应该也见到过有些网站在打开之后并没有直接加载出网站的所有内容&#xff0c;比如一些图片等等&#x…

Linux——进程通信

我们知道&#xff0c;进程具有独立性&#xff0c;各进程之间互不干扰&#xff0c;但我们为什么还要让其联系&#xff0c;建立通信呢&#xff1f;比如&#xff1a;数据传输&#xff0c;资源共享&#xff0c;通知某个事件&#xff0c;或控制某个进程。因此&#xff0c;让进程间建…

【免费参会合集】2025年生物制药行业展会会议表格整理

全文精心整理, 建议今年参会前都好好收藏着&#xff0c;记得点赞&#xff01; 医药人非常吃资源&#xff0c;资源从何而来&#xff1f;作为一名从事医药行业的工作者&#xff0c;可以很负责任的告诉诸位&#xff0c;其中非常重要的一个渠道就是会议会展&#xff01; 建议所有医…

腾讯云开发+MCP:旅游规划攻略

1.登录注册好之后进入腾讯云开发 2.创建环境 4.创建好环境之后点击去开发 5.进入控制台后&#xff0c;选择AI&#xff0c;找到MCP 6.点击创建MCP Server 使用腾讯云开发创建MCP目前需要云开发入门版99/月&#xff0c;我没开通&#xff0c;所以没办法往下进行。

Sklearn入门之数据预处理preprocessing

、 Sklearn全称:Scipy-toolkit Learn是 一个基于scipy实现的的开源机器学习库。它提供了大量的算法和工具&#xff0c;用于数据挖掘和数据分析&#xff0c;包括分类、回归、聚类等多种任务。本文我将带你了解并入门Sklearn下的preprocessing在机器学习中的基本用法。 获取方式…

家用打印机性价比排名及推荐

文章目录 品牌性价比一、核心参数对比与场景适配二、技术类型深度解析三、不同场景选择 相关文章 品牌 性价比 一、核心参数对比与场景适配 兄弟T436W 优势&#xff1a; 微压电技术&#xff0c;打印头寿命长&#xff0c;堵头率低。 支持A4无边距和5G WiFi&#xff0c;适合照片…

数字电子技术基础(四十七)——使用Mutlisim软件来模拟74LS85芯片

目录 1 使用74LS85N芯片完成四位二进制数的比较 1.1原理介绍 1.2 器件选择 1.3 运行电路 2 使用74LS85N完成更多位的二进制比较 1 使用74LS85N芯片完成四位二进制数的比较 1.1原理介绍 对于74LS85 是一款 4 位数值比较器集成电路&#xff0c;用于比较两个 4 位二进制数&…

关于STM32创建工程文件启动文件选择

注意启动文件只要选择这几个 而不是要把所有都选上

LLC电路工作在容性区的风险

在t0时刻之前&#xff0c;Q6Q7导通&#xff0c;回路如下所示&#xff0c;此时A点电压是低压&#xff0c;B点电压是高压 在t0时刻时&#xff0c;谐振电流相位发生变换&#xff0c;在t1时刻&#xff0c;Q5&#xff0c;Q8导通&#xff0c;对于Q8MOS管来说&#xff0c;B点电压在Q6Q…

Linux Kernel 6

clone 系统调用&#xff08;The clone system call&#xff09; 在 Linux 中&#xff0c;使用 clone() 系统调用来创建新的线程或进程。fork() 系统调用和 pthread_create() 函数都基于 clone() 的实现。 clone() 系统调用允许调用者决定哪些资源应该与父进程共享&#xff0c…