通讯录(基于单链表)

ops/2024/9/25 21:27:50/

通讯录(基于单链表)

我们知道 链表是由一个个节点组成的,我们让节点的数据域去存储一个结构体

这个结构体是存储联络人数据的一个结构体,里面放着许多信息:

// 要在链表的每一个节点中存储联系人数据 
// 那我们就要定义联系人的结构体
#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;

由于有了前面单链表的知识 我们这边直接放代码:

代码:

我们一共分为五个文件来完成这个通讯录,分别是之前单链表的SList.c,SList.h,

test.c(测试文件),Contact.h(通讯录头文件),Contact.c(通讯录功能实现)

SList.c:

# include"SList.h"
// 链表的功能函数实现都在这里// 链表节点的申请
ListNode* SLTBuyNode(SLTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc");return;}newnode->data = x;newnode->next = NULL;return newnode;
} 链表的打印
//void SLTPrint(ListNode* phead)
//{
//	// 遍历链表
//	ListNode* pcur = phead;
//	while (pcur)
//	{
//		printf("%d->", pcur->data);
//		pcur = pcur->next;
//	}
//	printf("NULL\n");
//}// 链表的尾插
void SLTPushBack(ListNode** pphead, SLTDataType x)
{assert(pphead);ListNode* newnode = SLTBuyNode(x); //申请节点if (*pphead == NULL){*pphead = newnode;}else{ListNode* pcur = *pphead;while (pcur){if (pcur->next == NULL){// 尾插pcur->next = newnode;break;}pcur = pcur->next;}}
}// 链表的头插
void SLTPushFront(ListNode** pphead, SLTDataType x)
{assert(pphead);ListNode* newnode = SLTBuyNode(x); // 申请节点// 头插ListNode* pcur = *pphead;newnode->next = pcur;*pphead = newnode;
}// 链表的尾删
void SLTPopBack(ListNode** pphead)
{assert(pphead);assert(*pphead);if ((*pphead)->next == NULL) // 只有一个节点{free(*pphead);*pphead = NULL;}else{ListNode* pcur = *pphead;ListNode* prev = *pphead;// 遍历链表找到尾节点while (pcur->next != NULL){prev = pcur;// prev指向pcur的前一个节点pcur = pcur->next;}// 走到这里说明pcur指向最后一个节点prev->next = NULL;free(pcur);pcur = NULL;}
}// 链表的头删
void SLTPopFront(ListNode** pphead)
{assert(pphead);assert(*pphead);//头删ListNode* next = (*pphead)->next;free(*pphead);*pphead = next;
} 链表的查找
//ListNode* SLTFind(ListNode* phead, SLTDataType x)
//{
//	assert(phead);
//
//	ListNode* pcur = phead;
//	// 遍历链表 查找
//	while (pcur)
//	{
//		if (pcur->data == x)
//		{
//			return pcur;
//		}
//		pcur = pcur->next;
//	}
//	// 走到这里说明没找到
//	return NULL;
//}// 在链表的指定位置之前插入数据
void SLTInsert(ListNode** pphead, ListNode* pos, SLTDataType x)
{assert(pphead);assert(*pphead);assert(pos);// 申请节点ListNode* newnode = SLTBuyNode(x);// 要分成头插 和其他情况if (pos == *pphead){SLTPushFront(pphead, x); // 引用头插}else{ListNode* pcur = *pphead;ListNode* prev = *pphead;while (pcur != pos){if (pcur == NULL){printf("不存在pos这个节点,请重新输入合法的值\n");return;}prev = pcur;// prev 指向pcur的前一个节点pcur = pcur->next;}// 走到这里说明pcur指向的就是pos节点// 完成链接 prev->newnode->posprev->next = newnode;newnode->next = pos;}
}// 在链表指定位置之后插入数据
void SLTInsertAfter(ListNode* pos, SLTDataType x) // 不需要改变第一个节点的位置 无需传入第一个节点的指针
{assert(pos);// 申请节点ListNode* newnode = SLTBuyNode(x);// 尾插  // 完成 pos->newnode->pos的下一个节点的链接newnode->next = pos->next;pos->next = newnode;
}// 删除链表指定位置的节点
void SLTErase(ListNode** pphead, ListNode* pos)
{assert(pphead);assert(*pphead);assert(pos);// 要分成第一个节点 和其他情况 if (pos == *pphead){// 头删SLTPopFront(pphead);}else{ListNode* pcur = *pphead;ListNode* prev = *pphead;// 遍历链表 找到pos节点while (pcur != pos){if (pcur == NULL){printf("没有找到pos节点,请重新输入合法值\n");return;}prev = pcur;pcur = pcur->next;}// 走到这里说明pcur指向的就是pos节点prev->next = pcur->next;free(pcur);pcur = NULL;}
}// 删除链表指定位置之后的节点
void SLTEraseAfter(ListNode* pos) // 不需要改变第一个节点的位置 无需传入**pphead
{assert(pos);assert(pos->next); // pos的下一个节点不能没有 不然怎么删除// 删除  链接 pos节点 和 pos的下个节点所存储的next指针ListNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;// 下面这段代码 也可以实现删除pos之后的节点的功能//SLTNode* next = pos->next->next;//free(pos->next);//pos->next = NULL;//pos->next = next;
}//  销毁链表
void SListDesTroy(ListNode** pphead)
{assert(pphead);assert(*pphead);ListNode* pcur = *pphead;ListNode* next = NULL;// 遍历链表 一个节点一个节点的销毁while (pcur){next = pcur->next;free(pcur);pcur = next;}// 走到这里说明删除完了*pphead = NULL;
}

SList.h

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1
# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
# include<string.h>
# include"Contact.h"// 节点的结构体定义
typedef peoInfo SLTDataType;
typedef struct ListNode
{SLTDataType data;struct ListNode* next;
}ListNode;// 节点的申请
ListNode* SLTBuyNode(SLTDataType x);// 链表的打印
void SLTPrint(ListNode* phead);//头部插⼊删除/尾部插⼊删除
void SLTPushBack(ListNode** pphead, SLTDataType x);
void SLTPushFront(ListNode** pphead, SLTDataType x);
void SLTPopBack(ListNode** pphead);
void SLTPopFront(ListNode** pphead);//查找
ListNode* SLTFind(ListNode* phead, SLTDataType x);//在指定位置之前插入数据
void SLTInsert(ListNode** pphead, ListNode* pos, SLTDataType x);//在指定位置之后插入数据
void SLTInsertAfter(ListNode* pos, SLTDataType x); // 不需要改变第一个节点的位置 无需传入第一个节点的指针//删除pos节点
void SLTErase(ListNode** pphead, ListNode* pos);//删除pos之后的节点
void SLTEraseAfter(ListNode* pos); // 不需要改变第一个节点的位置 无需传入**pphead//销毁链表
void SListDesTroy(ListNode** pphead);

Contact.h:

#pragma once// 要在链表的每一个节点中存储联系人数据 
// 那我们就要定义联系人的结构体
#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;//前置声明 
typedef struct ListNode 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);

Contact.c:

# include "SList.h"
# include "Contact.h"
// 通讯录的功能函数实现都在这里// 从文件中读取联系人数据到通讯录
void LoadContact(contact** con)
{// 打开文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("fopen");exit(1);}// 开始读取数据peoInfo info;// while循环每次从pf中拿出一个联络人数据放到info中 while (fread(&info, sizeof(peoInfo), 1, pf)){// 将info尾插到链表中  也就是放到通讯录中SLTPushBack(con, info);}// 关闭文件fclose(pf);pf = NULL;
}// 初始化通讯录
void InitContact(contact** con)
{LoadContact(con); // 从文件中读取联络人数据到通讯录中
}//展示通讯录数据
void PenInfoPrint(peoInfo x)
{printf("%s %s %d %s %s\n", x.name, x.gender, x.age, x.tel, x.addr);
}
void ShowContact(contact* con)
{contact* cur = con;printf("该通讯录的联系人信息如下:\n");printf("姓名  性别  年龄  电话  地址\n"); // 表头while (cur){PenInfoPrint(cur->data);cur = cur->next;}
}//添加通讯录数据
void AddContact(contact** con) // 不能传未初始化的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);// 将数据添加到通讯录中SLTPushBack(con, info);printf("插入成功\n");
}//删除通讯录数据
contact* FindByName(contact* con,char name[])
{contact* cur = con;// 遍历通讯录 查看是否有名字相同的while (cur){if (strcmp(cur->data.name, name) == 0){return cur;}cur = cur->next;}//  走到这里说明没找到return NULL;
}
void DelContact(contact** con) //链表的删除指定位置的数据
{// 输入要删除的联络人的姓名char name[NAME_MAX] = { 0 };printf("请选择你要删除的联络人姓名\n");scanf("%s", name);// 我们根据输入的姓名去查找通讯录中是否有该姓名的存在  有就删除 contact* ret = FindByName(*con, name);if (ret != NULL){SLTErase(con, ret);printf("你所要删除的联系人:%s,已经成功删除\n", name);}else{printf("你所要删除的联系人,不存在于通讯录中!!!\n");return;}
}//查找通讯录数据
void FindContact(contact* con)
{// 输入要查找的联络人姓名char name[NAME_MAX] = { 0 };printf("请输入你要查找的联络人姓名\n");scanf("%s", name);contact* ret = FindByName(con, name);if (ret != NULL){printf("你所查找的联络人:%s,信息如下:\n", name);printf("姓名  性别  年龄  电话  地址\n"); // 表头PenInfoPrint(ret->data);}else{printf("你所查找的联络人:%s,不存在!!!\n", name);return;}
}//修改通讯录数据
void ModifyContact(contact** con)
{char name[NAME_MAX] = { 0 };printf("请输入你要修改的联络人姓名\n");scanf("%s", name);contact* ret = FindByName(*con, name);if (ret == NULL){printf("你所查找的联络人:%s,不在通讯录中\n", name);return;}else{// 找到了printf("该联系人的信息如下:\n");printf("姓名  性别  年龄  电话  地址\n"); // 表头PenInfoPrint(ret->data);// 修改 让用户自主选择修改那一部分int n = 0;do {printf("请选择你需要修改的数据\n");printf("*****************************************\n");printf("****1.姓名*******2.性别******3.年龄*******\n");printf("****4.电话*******5.地址******0.退出*******\n");printf("*****************************************\n");scanf("%d", &n);switch (n){case 1:printf("请输入新的姓名\n");scanf("%s", ret->data.name);break;case 2:printf("请输入新的性别\n");scanf("%s", ret->data.gender);break;case 3:printf("请输入新的年龄\n");scanf("%d", &(ret->data.age));break;case 4:printf("请输入新的电话\n");scanf("%s", ret->data.tel);break;case 5:printf("请输入新的地址\n");scanf("%s", ret->data.addr);break;case 0:printf("已退出修改\n");break;default:printf("输入不合法,请重新输入\n");break;}} while (n);printf("修改成功\n");}
}// 将通讯录数据写入文件中
void SaveContact(contact* con)
{// 打开文件FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen");exit(1);}// 将通讯录数据写入文件中// 遍历通讯录contact* cur = con;while (cur){fwrite(&(cur->data), sizeof(peoInfo), 1, pf);cur = cur->next;}printf("成功保存联络人数据!\n");// 关闭文件fclose(pf);pf = NULL;
}//销毁通讯录数据
void DestroyContact(contact** con)
{SListDesTroy(con);
}

test.c:

# include "SList.h"void menu()
{printf("******************通讯录**********************\n");printf("********1.增加联系人   2.删除联系人************\n");printf("********3.修改联系人   4.查找联系人************\n");printf("********5.展示联系人   0.退出******************\n");
}int main()
{//Test01();int n = 0;contact* con = NULL;while (1){int m = 0;printf("是否要导入历史数据?\n");printf("1.yes,2.no\n");scanf("%d", &m);if (m == 1){LoadContact(&con);printf("导入历史数据成功\n");break;}else if (m == 2){printf("已退出......\n");break;}else{printf("输入不合法,请重新输入\n");}}do {menu();printf("请输入你要选择的操作\n");scanf("%d", &n);switch (n){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:printf("已退出......\n");break;default:printf("操作不合法,请重新输入\n");break;}} while (n);while (1){int m = 0;printf("是否要保存联系人数据?\n");printf("1.yes,2.no\n");scanf("%d", &m);if (m == 1){SaveContact(con); // 将联络人信息存储到文件中break;}else if (m == 2){printf("已退出......\n");break;}elseprintf("输入不合法,请重新输入\n");}DestroyContact(&con);// 将通讯录销毁return 0;
}

这里我们优化了一下代码,让用户使用的时候更有体验。


http://www.ppmy.cn/ops/29501.html

相关文章

DNS、ICMP、NAT以及代理服务器

目录 1. DNS 1.1. DNS 背景 1.2. 域名简介 1.3. 域名解析过程 2. ICMP 2.1. ICMP 的功能 2.2. ICMP 的报文格式 2.3. ping 命令 2.4. traceroute 命令 3. NAT和代理服务器 3.1. NAT 技术 3.2. NAT IP转换过程 3.3. NAT 技术的缺陷 3.4. 代理服务器 3.4.1. 正向…

C语言双向链表如何实现向指定索引位置插入元素

核心代码&#xff1a; int insertDoubleLinkedList(DoubleLinkedList *list, int index, int value) {if (list NULL || list->head NULL || list->tail NULL) {return -1;}// 当一个元素都没有的时候&#xff0c;或者indexsize的时候&#xff0c;是支持插入的if (in…

C++@vscode配置C++开发环境常见问题和实践经验

文章目录 abstractvscode配置C/C开发环境常见问题 FAQC/C共用一组tasks.json/launch.json文件?关于配置文件中的注释更快地编译运行调试时调用外部终端控制台二次编译失败问题编译多个源文件&#x1f60a;源文件组织 编译出的可执行文件名中文乱码&#x1f60a;修改tasks.json…

unity制作app(3)--gps定位

1.unity中定位Unity之GPS定位&#xff08;高德解析&#xff09;_unity gps定位-CSDN博客 代码需要稍微修改一下&#xff0c;先把脚本绑到一个button上试一试&#xff01; 2.先去高德地图认证&#xff08;app定位&#xff09; 创建应用和 Key-Web服务 API | 高德地图API (ama…

leetcode 174.地下城游戏

思路&#xff1a;dp。 原先的时候其实是想这样用dfs的做法进行解答的&#xff0c;但是呢&#xff0c;是不对的。 这里作者dfs的思路是&#xff1a;首先找出来最小路径和&#xff0c;然后再处理最小路径和这条路径里面的初始值。但是&#xff0c;后来发现这样不一定是最优解&a…

C++奇迹之旅:C++内存管理的机制初篇

文章目录 &#x1f4dd;C/C内存分布&#x1f320; C语言中动态内存管理方式&#x1f309;C内存管理方式 &#x1f320;new/delete操作内置类型&#x1f309;C与C链表构建对比 &#x1f6a9;总结 &#x1f4dd;C/C内存分布 这是C/C中程序内存区域划分图&#xff1a; 数据段&am…

基于php+mysql+html图书管理系统(含实训报告)

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

速盾:cdn真的抗打吗?

CDN&#xff08;Content Delivery Network&#xff09;是一种分布式网络架构&#xff0c;旨在提供高速、可靠的内容传输服务。它通过将内容存储在位于全球各地的服务器上&#xff0c;使用户可以从最近的服务器获取内容&#xff0c;从而提高访问速度和性能。然而&#xff0c;尽管…