通讯录(基于单链表)

通讯录(基于单链表)

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

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

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

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

相关文章

java发送请求2次开发-get请求json

因为你请求参数不为空&#xff0c;接口都会把这个参数带上 所以借鉴HttpPost类 继承这个类&#xff0c; 这个类是可以带消息的 httpgetwithentity&#xff0c;httpget请求带上消息 复写 构造方法复制过来进行使用 二次开发类让其get请求时可以发送json

基于大爆炸优化算法的PID控制器参数寻优matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于大爆炸优化算法的PID控制器参数寻优matlab仿真。对比优化前后的PID控制输出。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB2022a .....................…

测试腾讯云的高防CC抵御了攻击

网站需要安装防止CC攻击&#xff0c;因为CC攻击是一种常见的网络攻击&#xff0c;它会对网站造成严重的影响。 CC攻击&#xff08;Cybercrime Control&#xff09;是指向网站发起大量虚假请求的攻击&#xff0c;目的是使网站的资源耗尽&#xff0c;无法正常运行。CC攻击与DDoS…

Linux 查看所有进程 ps -ef 命令详解, 查看网络链接信息 netstat -tuplan 命令详解

ps -ef 指令 在Linux系统中&#xff0c;ps 是一个非常基础且强大的命令行工具&#xff0c;用于报告当前系统中运行的进程的状态。它是 “Process Status” 的缩写。通过使用不同的选项和参数&#xff0c;ps 命令能够以多种格式展示进程信息&#xff0c;帮助用户了解系统中进程…

python-pytorch官方示例Generating Names with a Character-Level RNN的部分理解0.5.03

pytorch官方示例Generating Names with a Character-Level RNN的部分理解 模型结构功能关键技术模型输入模型输出预测实现 模型结构 功能 输入一个类别名和一个英文字符&#xff0c;就可以自动生成这个类别&#xff0c;且以英文字符开始的姓名 关键技术 将字符进行one-hot编…

通过符号程序搜索提升prompt工程

原文地址&#xff1a;supercharging-prompt-engineering-via-symbolic-program-search 通过自动探索​​大量提示变体来找到更好的提示 2024 年 4 月 22 日 众所周知&#xff0c;LLMs的成功在很大程度上仍然取决于我们用正确的指导和例子来提示他们的能力。随着新一代LLMs变得越…

JAVA面试题---WEB部分

网络通讯 TCP与UDP TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的、 可靠的、 基于 IP 的传输层协议。 UDP 是 User Datagram Protocol 的简称&#xff0c;中文名是用户数据报协议&#xff0c;是 OSI 参考模 型中的传输层协议&#xff0c;它是…

在图像处理领域,机器学习方法和深度学习方法的优势

在图像处理领域&#xff0c;机器学习方法和深度学习方法都被广泛应用&#xff0c;但两者有一些不同点和各自的优势。 机器学习 机器学习方法是利用数据和统计学方法来构建模型和算法&#xff0c;从而对图像进行分类、分割、特征提取等任务。常见的机器学习方法包括支持向量机…

【杂谈】记录代码中遇到的问题(持续更新中)

写代码时经常遇到各种各样的bug&#xff0c;有时候很难发现&#xff0c;下面是我自己的一些见解 1.数组开在全局区和栈区所能开到的最大范围是不一样的 main函数里的数组&#xff0c;范围最大可以开到1e5 全局区的函数最大可以开到1e8的范围 2.#define int long long 写#dein…

【Linux】常用命令大揭秘,轻松驾驭终端世界

常见命令大全 概念1.1&#xff1a;开源、闭源的区别1.2&#xff1a;应用场景 发行版XShell3.1&#xff1a;使用XShell登入主机3.2&#xff1a;普通用户的增加、删除3.3&#xff1a;查看账户的信息whoami指令who指令 文件和目录基本命令4.1&#xff1a;指令的周边知识文件路径Li…

4月份全球市场推出的18款网络安全热点产品和服务:生成式AI应用主导安全产品创新

CSO在线追踪了4份全球市场推出的18代表性网络安全产品和服务&#xff0c;从中可以观察网络安全产品创新趋势和风向。 1、Salt Security 在其API保护平台添加 OAuth安全产品 4月25日&#xff1a;Salt Security 在其API保护平台中添加了新的 OAuth 安全产品&#xff0c;以帮助组…

contentprovider一直报错,发现是android 的manifest中 provider 放在activity后面不行

在Android应用的AndroidManifest.xml文件中&#xff0c;所有的组件&#xff08;如activity、service、receiver和provider&#xff09;必须在该文件中按正确的顺序声明。在Android 8.0&#xff08;API 级别 26&#xff09;及更高版本中&#xff0c;如果provider组件放置在activ…

css响应式布局左、右上、右中布局

一、布局效果 二、布局代码 <div class"parent"><div class"left">菜单</div><div class"right"><div class"right-top">顶部导航</div><div class"right-content"></div>…

一个C++小程序调试过程记录

Top 20 C Projects With Source Code [2024 Update]https://www.interviewbit.com/blog/cpp-projects/ 这个网页有一些简单的C程序的源码&#xff0c;闲来无事&#xff0c;把第一个程序&#xff08;Bookshop Management System Using C&#xff09;的源码下载了下来。 源文件…

文件上传结合springboot

目录 前台页面 后台接口 文件上传总结 前端页面三要素 服务端接收文件 前台页面 <form action"/upload" method"post" enctype"mutipart/form-data"> 姓名<imput type"text" name"username"><br> 年…

Mybatis进阶(映射关系多对一 )

文章目录 1.需求分析2.应用实例&#xff08;xml配置&#xff09;1.数据表设计2.entity设计&#xff08;不要使用toString会栈溢出&#xff09;1.Pet.java2.User.java 3.编写Mapper1.PetMapper.java2.UserMapper.java 4.编写Mapper.xml1.UserMapper.xml2.PetMapper.xml 5.测试Us…

基于缓存注解的时间戳令牌防重复提交设计

文章目录 一&#xff0c;概述二&#xff0c;实现过程1、引入pom依赖2、定义缓存管理3、时间戳服务类4、模拟测试接口 三&#xff0c;测试过程1&#xff0c; 模拟批量获取2&#xff0c; 消费令牌 四&#xff0c;源码放送五&#xff0c;优化方向 一&#xff0c;概述 API接口由于…

uniapp 异步加载级联选择器(Cascader,data-picke)

目录 Props 事件方法 inputChange事件回调参数说明&#xff1a; completeChange事件回调参数说明&#xff1a; temList 属性Object参数说明 defaultItemList 属性Object参数说明 在template中使用 由于uniapp uni-ui的data-picke 不支持异步作者自己写了一个 插件市场下…

vue3 ——笔记 (表单输入,监听器)

表单输入 在Vue 3中&#xff0c;v-model指令的用法稍有不同于Vue 2。在Vue 3中&#xff0c;v-model指令实际上是一个语法糖&#xff0c;它会自动将value属性和input事件绑定到组件或元素上&#xff0c;以实现双向数据绑定。 在自定义组件中使用v-model时&#xff0c;需要在组…

身份证号对应地区信息-MySQL

这里写自定义目录标题 MySQL表结构MySQL表对应数据 MySQL表结构 CREATE TABLE idcard_contrast (code varchar(2000) NOT NULL COMMENT 身份证前六位,value varchar(3000) DEFAULT NULL COMMENT 对应地址 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT身份证对照表;MySQL表对…