【(数据结构)—— 基于单链表实现通讯录】

(数据结构)—— 基于单链表实现通讯录

  • 一.通讯录的功能介绍
    • 1.基于单链表实现通讯录
      • (1). 知识要求
      • (2). 功能要求
  • 二.通讯录的代码实现
    • 1.通讯录的底层结构(单链表)
      • (1).思路展示
      • (2).底层代码实现(单链表)
        • 1.单链表头文件 —— (函数的定义)
        • 2.单链表的源文件 ——(函数的实现)
    • 2.通讯录上层代码实现(通讯录结构)
      • (1)思路展示
      • (2) 上层代码的实现(顺序表)
        • 1.通讯录的头文件 —— (函数的定义)
        • 2.通讯录的源文件 —— (函数的实现)
        • 3.测试源文件 —— (功能函数的测试运行)
    • 3.通讯录代码运行展示(数据只用于测试,无实际意义)
      • (1) 测试展示
      • (2) 总结升华

一.通讯录的功能介绍

1.基于单链表实现通讯录

(1). 知识要求

C语言基础要求:结构体、动态内存管理、单链表、文件件操作

(2). 功能要求

1)至少能够存储100个人的通讯信息
2)能够保存用户信息:名字、性别、年龄、电话、地址等
3)增加联系人信息
4)删除指定联系人
5)查找制定联系人
6)修改指定联系人
7)显示联系人信息

二.通讯录的代码实现

1.通讯录的底层结构(单链表)

(1).思路展示

由于,通讯录是基于单链表实现的,所以掌握基本的单链表的结构代码,非常重要~!

(2).底层代码实现(单链表)

1.单链表头文件 —— (函数的定义)
SList.h
#pragma once
#include"contact.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//创建链表节点结构
typedef struct ContactInfo SLDatatype;
typedef struct SListNode
{SLDatatype data;//要保存的数据struct SListNode* next;
}SLNode;//创建几个节点组成链表,并且打印链表
//打印
void SLNPrint(SLNode* phead);//尾插
void SLNPushBack(SLNode** pphead, SLDatatype x);
//头插
void SLNPushFront(SLNode** pphead, SLDatatype x);
//尾删
void SLNPopBack(SLNode** pphead);
//头删
void SLNPopFront(SLNode** pphead);
//在指定位置插入删除
// 
//查找指定的pos
SLNode* SLNFind(SLNode** pphead, SLDatatype x);
//在指定位置之前插入
void SLNInsrt(SLNode** pphead, SLNode* pos, SLDatatype x);
//在指定位置之后插入删除
void SLNInsrtAfter(SLNode* pos, SLDatatype x);
//删除指定位置的数据
void SLNErase(SLNode** pphead, SLNode* pos);
//删除指定位置之后的数据
void SLNEraseAfter(SLNode* pos);
//销毁链表
void SLNDestroy(SLNode** pphead);
2.单链表的源文件 ——(函数的实现)
SList.c
#include"SList.h"
//打印
//void SLNPrint(SLNode* phead)
//{
//	//循环打印
//	//可以用phead直接来访问,但是注意,当代码走到
//	//第16行的时候,此时phead已经变成NULL了
//	//若代码没写完,我还要继续使用指向第一个节点的地址时,这时我就
//	//找不到第一个节点的地址
//	//所以为了避免第一个节点被更改,要定义一个新的来代替
//	SLNode* pcur = phead;
//	while (pcur)
//	{
//		printf("%d ->", pcur->data);
//		pcur = pcur->next;
//	}
//	printf("NULL\n");
//}
//创建新的节点
SLNode* SLByNode(SLDatatype x)
{//开辟新的空间SLNode* node = (SLNode*)malloc(sizeof(SLNode));node->data = x;node->next = NULL;return node;
}
//尾插
void SLNPushBack(SLNode** pphead, SLDatatype x)
{SLNode* node = SLByNode(x);//判断链表是否为空,如果为空,直接插入if (*pphead == NULL){*pphead = node;return;}//说明链表不为空 ,找尾SLNode* pcur = *pphead;while (pcur->next != NULL){pcur = pcur->next;}pcur->next = node;}//头插
void SLNPushFront(SLNode** pphead, SLDatatype x)
{//创建新的节点SLNode* node = SLByNode(x);//让新的节点跟头节点连起来node->next = *pphead;//再让新节点成为头节点*pphead = node;}//尾删
void SLNPopBack(SLNode** pphead)
{//先判断链表是否为空,如果为空就无法进行删除操作assert(pphead);//还要判断第一个节点不能为空assert(*pphead);//只有一个节点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;return;}//有多个节点的情况//先找到尾节点/尾节点的前一个节点SLNode* ptail = *pphead;SLNode* prev = NULL;while (ptail->next){prev = ptail;ptail = prev->next;}//让尾节点的前一个节点指向不再指向尾节点,而是指向尾节点的下一个节点 prev->next = ptail->next;//然后在删除尾节点free(ptail);//为什么要将ptaii置为空指针?代码后面明明没有再使用ptail,因为打印链表的代码有判断节点的地址是否为空ptail = NULL;}void SLNPopFront(SLNode** pphead)
{//判断链表不能为空assert(pphead);//判断头节点不能为空assert(*pphead);SLNode* del = *pphead;*pphead = (*pphead)->next;free(del);//出于代码规范del = NULL;}//查找指定的pos
//SLNode* SLNFind(SLNode** pphead, SLDatatype x)
//{
//	assert(pphead);
//	SLNode* pcur = *pphead;
//	while (pcur)
//	{
//		if (pcur->data == x)
//		{
//			return pcur;
//		}
//		pcur = pcur->next;
//	}
//	//出循环,代表没找到
//	return NULL;
//}
//在指定位置之前插入
void SLNInsrt(SLNode** pphead, SLNode* pos, SLDatatype x)
{SLNode* node = SLByNode(x);assert(pphead);assert(*pphead);//只有一个节点的情况 == pos就是头节点,直接进行头插if (pos == *pphead){node->next = *pphead;*pphead = node;return;}//有多个节点的情况//找pos的前一个节点SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//这里的插入节点的处理顺序可以调换prev->next = node;node->next = pos;}//在指定的位置之后插入
void SLNInsrtAfter(SLNode* pos, SLDatatype x)
{assert(pos);SLNode* node = SLByNode(x);// node  pos  node->nextnode->next = pos->next;pos->next = node;
}//删除指定位置的节点
void SLNErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead){*pphead = (*pphead)->next;free(pos);return;}//找pos的前一个节点SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
//删除指定位置之后的节点
void SLNEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//销毁链表
void SLNDestroy(SLNode** pphead)
{assert(*pphead);SLNode* pcur = *pphead;while (pcur){SLNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

2.通讯录上层代码实现(通讯录结构)

(1)思路展示

1)由于,通讯录是基于单链表实现的。
2)所以,通讯录上层代码,只需要在上层代码的头文件代码中创建一个保存联系人的结构,然后再定义通讯录功能函数即可。
3)接着在通讯录上层代码的源文件中将这些功能函数实现即可。
4)基于前面,我们已经知道,通讯录是单链表实现的,所以在实现通讯录功能函数时,只需要在通讯录上层代码的源文件中,加上单链表的头文件就可以调运单链表的函数来实现通讯录了。

(2) 上层代码的实现(顺序表)

1.通讯录的头文件 —— (函数的定义)
contact.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 15
#define ADDR_MAX 100//前置声明
typedef struct SListNode contact;//用户数据
typedef struct ContactInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tel[TEL_MAX];char addr[ADDR_MAX];
}CInfo;//初始化通讯录
void InitContact(contact** pcon);
//添加通讯录数据
void AddContact(contact** pcon);
//删除通讯录数据
void DelContact(contact** pcon);
//展示通讯录数据
void ShowContact(contact* pcon);
//查找通讯录数据
void FindContact(contact* pcon);
//修改通讯录数据
void ModifyContact(contact** pcon);
//销毁通讯录数据
void DestroyContact(contact** pcon);
2.通讯录的源文件 —— (函数的实现)
contact.c
#include"contact.h"
#include"SList.h"void LoadContact(contact** pcon) {FILE* pf = fopen("contact.txt", "rb");if (pf == NULL) {perror("fopen error!\n");return;}//循环读取文件数据CInfo info;while (fread(&info, sizeof(info), 1, pf)){SLNPushBack(pcon, info);}printf("历史数据导入通讯录成功!\n");
}
//初始化通讯录/建立头节点
void InitContact(contact** pcon)
{LoadContact(pcon);
}//添加通讯录数据
void AddContact(contact** pcon)
{//接下来获取的都是结构体CInfo结构体里要求的数据CInfo info;printf("请输入联系人姓名:\n");scanf("%s", info.name);printf("请输入联系人性别:\n");scanf("%s", info.sex);printf("请输入联系人年龄:\n");scanf("%d", &info.age);printf("请输入联系人电话:\n");scanf("%s", info.tel);printf("请输入联系人住址:\n");scanf("%s", info.addr);//联系人数据都获取到了,并保存到了结构体info中//往通讯录(链表)中插入数据SLNPushBack(pcon, info);
}
contact* FindByName(contact* pcon, char name[])
{contact* cur = pcon;while (cur){if (strcmp(cur->data.name, name) == 0) {return cur;}cur = cur->next;}return NULL;}//删除通讯录数据
void DelContact(contact** pcon)
{//直接强制用户使用名称来查找printf("请输入要删除的联系人姓名:\n");char name[NAME_MAX];scanf("%s", name);contact* findidex = FindByName(*pcon, name);if (findidex){(*pcon)->next = findidex->next;//找到了,要删除findidex位置的数据SLNErase(pcon, findidex);printf("删除成功\n");}else {printf("要删除的联系人不存在!\n");return;}}
//展示通讯录数据
void ShowContact(contact* pcon)
{contact* cur;cur = pcon;//打印表头printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");while (cur){printf("%-5s %-5s %-4d %-4s %-4s\n",cur->data.name,cur->data.sex,cur->data.age,cur->data.tel,cur->data.addr);cur = cur->next;}}
//查找通讯录数据
void FindContact(contact* pcon)
{//直接强制用户使用名称来查找printf("请输入你要查找的联系人\n");char name[NAME_MAX];scanf("%s", name);contact* findidex = FindByName(pcon, name);if (findidex==NULL){printf("没有此联系人\n");return;}else{printf("找到了:\n");printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-5s %-5s %-4d %-4s %-4s\n",findidex->data.name,findidex->data.sex,findidex->data.age,findidex->data.tel,findidex->data.addr);}
}//修改联系人选项
void MondifyMenu()
{printf("***************修改联系人选项****************\n");printf("***** 1.修改联系人名字  2.修改联系人性别*****\n");printf("***** 3.修改联系人年龄  4.修改联系人电话*****\n");printf("***** 5.修改联系人住址  0.退出删除选项*******\n");printf("******************************************\n");
}
//修改通讯录数据
void ModifyContact(contact** pcon)
{//直接强制用户使用名称来查找printf("请输入你要修改的联系人\n");char name[NAME_MAX];scanf("%s", name);contact* findidex = FindByName(*pcon, name);if (findidex == NULL){printf("没有你要删除的联系人\n");return;}else {printf("找到了:\n");printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-5s %-5s %-4d %-4s %-4s\n",findidex->data.name,findidex->data.sex,findidex->data.age,findidex->data.tel,findidex->data.addr);int val = -1;do {MondifyMenu();printf("请选择你需要修改的信息:\n");scanf("%d", &val);switch (val){case 1:printf("请输入修改后的联系人姓名:\n");scanf("%s", findidex->data.name);break;case 2:printf("请输入修改后的联系人性别:\n");scanf("%s", findidex->data.sex);break;case 3:printf("请输入修改后的联系人年龄:\n");scanf("%d", &findidex->data.age);break;case 4:printf("请输入修改后的联系人电话:\n");scanf("%s", findidex->data.tel);break;case 5:printf("请输入修改后的联系人住址:\n");scanf("%s", findidex->data.addr);break;case 0:printf("修改成功\n");printf("已退出删除联系人选项\n");break;default:printf("没有找到要修改的选项 请重新选择:\n");break;}} while (val != 0);}}
void SaveContact(contact* pcon) {FILE* pf = fopen("contact.txt", "wb");if (pf == NULL) {perror("fopen error!\n");return;}//将通讯录数据写入文件contact* cur = pcon;while (cur){fwrite(&(cur->data), sizeof(cur->data), 1, pf);cur = cur->next;}printf("通讯录数据保存成功!\n");
}void DestroyContact(contact** pcon) {SaveContact(*pcon);SLNDestroy(pcon);
}
3.测试源文件 —— (功能函数的测试运行)
test.c
#include"contact.h"
#include"SList.h"void menu()
{printf("***************通讯录****************\n");printf("***** 1.添加联系人  2.删除联系人*****\n");printf("***** 3.修改联系人  4.查找联系人*****\n");printf("***** 5.查看通讯录  0.退出通讯录*****\n");printf("*************************************\n");
}int main()
{int op = -1;contact* con=NULL;InitContact(&con);do {menu();printf("请选择你要进行的操作:\n");scanf("%d", &op);switch (op){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 (op != 0);DestroyContact(&con);return 0;
}

3.通讯录代码运行展示(数据只用于测试,无实际意义)

(1) 测试展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2) 总结升华

这里只是展示基于单链表实现通讯录的大致框架,和一些基本功能,有兴趣,能力的小伙伴,也可以下去拓展一下通讯录的其他功能,优化的更加完善,美观的通讯录。
感谢学习!

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

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

相关文章

Hive安装配置 - 内嵌模式

文章目录 一、Hive运行模式二、安装配置内嵌模式Hive&#xff08;一&#xff09;下载hive安装包&#xff08;二&#xff09;上传hive安装包&#xff08;三&#xff09;解压缩hive安装包&#xff08;四&#xff09;配置hive环境变量&#xff08;五&#xff09;关联Hadoop&#x…

【超详细】CentOS 7安装MySQL 5.7【安装及密码配置、字符集配置、远程连接配置】

准备工作&#xff1a;CentOS 7系统&#xff0c;并确保可以联通网络 1、获取MySQL 5.7 Community Repository软件包 注意&#xff1a;这里使用的是root用户身份。 wget https://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm2、安装软件包 rpm -ivh mysql5…

华为荣耀手机,开启开发者选项,hbuilder调试依然找不到

我的手机是华为荣耀50&#xff0c;其他华为手机不知道是不是这个问题哦 解决办法&#xff1a;usb配置&#xff0c;选择音频来源 然后就可以了

2020年江西省职业院校技能大赛高职组“信息安全管理与评估”赛项任务书样题

2020年江西省职业院校技能大赛高职组 “信息安全管理与评估”赛项任务书 样题 赛项时间 9:00-12:00&#xff0c;共计3小时。 赛项信息 赛项内容 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 平台搭建与安全设备配置防护 任务1 网络平台搭建 9:00-12:00 1…

MS17010(永恒之蓝)漏洞实战

曾因苦难多壮志&#xff0c;不教红尘惑坚心。 工具检测 实战过程 使用搜索命令&#xff0c;搜索ms17_010 search ms17_010 搜索网段中主机漏洞 use auxiliary/scanner/smb/smb_ms17_010 照例&#xff0c;show options 看一下配置 设置网段&#xff0c;run运行就行了 使用攻…

浅谈云原生

目录 1. 云原生是什么&#xff1f; 2. 云原生四要素 2.1 微服务 2.2 容器化 2.3 DevOps 2.4 持续交付 3. 具体的云原生技术有哪些&#xff1f; 3.1 容器 (Containers) 3.2 微服务 (Microservices) 3.3 服务网格 (Service Meshes) 3.4 不可变基础设施 (Immutable Inf…

Debian衍生桌面项目SpiralLinux12.231001发布

导读SpiralLinux 是一个从 Debian 衍生出来的桌面项目&#xff0c;其重点是在所有主要桌面环境中实现简洁性和开箱即用的可用性。 spiral Linux 是为刚接触 Linux 世界的人们量身定制的发行版。这是 GeckoLinux 开发人员的创意&#xff0c;他更喜欢保持匿名。尽管他不愿透露姓…

网安周报|OpenSSF 推出恶意软件包存储库

1.OpenSSF 推出恶意软件包存储库 为了应对恶意开源软件包日益增长的威胁&#xff0c;开源安全基金会 ( OpenSSF ) 推出了一项名为“恶意软件包存储库”的新计划。该存储库可能会成为打击恶意代码的主要参与者&#xff0c;旨在增强开源软件生态系统的安全性和完整性。该存储库已…

linux-守护进程daemon

linux-守护进程daemon 代码实现 main.c运行结果 代码实现 main.c //pName&#xff1a;程序名 //facility&#xff1a; 守护进程&#xff0c;输出日志类型 302页 #include<signal.h> #include<syslog.h> #include<fcntl.h> static int daemon_proc 0; #defin…

景联文科技语音数据标注:AUTO-AVSR模型和数据助力视听语音识别

ASR、VSR和AV-ASR的性能提高很大程度上归功于更大的模型和训练数据集的使用。 更大的模型具有更多的参数和更强大的表示能力&#xff0c;能够捕获到更多的语言特征和上下文信息&#xff0c;从而提高识别准确性&#xff1b;更大的训练集也能带来更好的性能&#xff0c;更多的数据…

数据结构-----图(graph)的储存和创建

目录 前言 图的储存结构 1.邻接矩阵 无向图的邻接矩阵 有向图的邻接矩阵 网&#xff08;赋权图&#xff09;的邻接矩阵 代码表示 2.邻接表 无向图的邻接表 有向图的邻接表 代码表示 3.邻接矩阵和邻接表对比 邻接矩阵 邻接表 图的创建 1.邻接矩阵创建图&#xff0…

原子核内的相互作用

原子核内的相互作用 氘核基态 和态的混合 核子-核子散射 低能核子-核子散射 n-p散射&#xff1a;只有核力 p-p散射&#xff1a;较复杂 n-n散射&#xff1a;n-n散射没有直接实验 低能 p-p 散射和核力的电荷无关性 高能核子-核子散射 核力的主要性质 核力主要性质 核力是短程力…

国旗升降系统程序及原理图资料

本文主要介绍国旗升降系统设计程序及原理图&#xff08;完整资料见文末链接&#xff09; 系统原理图如下&#xff0c;程序资料见文末 附完整资料链接 百度网盘链接: https://pan.baidu.com/s/1Q5J2J8LgVJ-hoeTSVP95_g?pwd3qkw 提取码: 3qkw

js鼠标点击添加图标并获取图标的坐标值

给这个图片添加摄像头图标&#xff0c;并获取图标的坐标值&#xff0c;也就是图标的css样式是positon:absolute,获取left和top的值。 图片1 思路是这样的&#xff0c;获取这里的长度&#xff0c; 图片2 1.鼠标点击时距浏览器的左边距离和上边距离&#xff0c;相当于(0,0)坐标 …

零基础搭建个人网站详细流程

最近两天&#xff0c;为了给自己的工具类APP备案&#xff0c;买了阿里云ECS和域名。虽然很想说离线工具APP不用联网&#xff0c;但是现实就很无语。言归正传&#xff0c;既然买了总不能将它们闲置着&#xff0c;就诞生了建站的想法&#xff0c;至少还能放个用户协议和隐私协议。…

『ARM』和『x86』处理器架构解析指南

前言 如果问大家是否知道 CPU&#xff0c;我相信不会得到否定的答案&#xff0c;但是如果继续问大家是否了解 ARM 和 X86 架构&#xff0c;他们的区别又是什么&#xff0c;相信可能部分人就会哑口无言了 目前随着深度学习、高性能计算、NLP、AIGC、GLM、AGI 的技术迭代&#…

【c语言】atoi的模拟实现

1.头文件 atoi() 是 C语言的一个标准库函数&#xff0c;定义在<stdlib.h>头文件中 2.atoi的解析 具体来讲&#xff0c;atoi() 函数首先会丢弃尽可能多的空白字符&#xff0c;直至找到第一个非空白字符&#xff0c;然后从该字符开始&#xff0c;识别 “”、“-” 以及 …

【脉冲通信】用于空间应用的飞秒脉冲通信的符号误码率模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

TCP通信实战:模拟BS系统

1、之前的客户端都是什么样的 其实就是CS架构&#xff0c;客户端是需要我们自己开发实现的 2、BS结构是什么样的&#xff0c;需要开发客户端吗&#xff1f; 浏览器访问服务端&#xff0c;不需要开发客户端 注意&#xff1a;服务端必须给浏览器响应HTTP协议格式的数据&#xff0…

[swift刷题模板] 树状数组(BIT/FenwickTree)

[TOC]([swift刷题模板] 树状数组(BIT/FenwickTree) ) 一、 算法&数据结构 1. 描述 [python刷题模板] 树状数组 二、 模板代码 1. 单点赋值(增加)&#xff0c;区间求和(PURQ) 例题: 307. 区域和检索 - 数组可修改 class BIT {var c: [Int]var n: Int init(_ n: Int){c…