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

发布于:2025-04-20 ⋅ 阅读:(10) ⋅ 点赞:(0)

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

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查看完整源代码


网站公告

今日签到

点亮在社区的每一天
去签到