数据结构:用顺序表和单链表实现通讯录(上)

前言

首先简要介绍顺序表和链表的概念和区别以作区分。

顺序表:逻辑上是线性的,物理性质上也是线性的。逻辑是线性的(连续的)体现在它可以通过第一个数找到接下来的数。物理性质上的线性体现在分配给它的内存是连续的。它本质上就像一个数组,可以通过下标来访问成员。

单链表:这里说的单链表是指不带头单向不循环链表。链表和顺序表是不同的。链表在逻辑上是线性的,但在物理性质上是非线性的。需要的时候申请一块内存,但这块内存和其他内存不一定连续,它可能是散落在四周的,只是通过记住下一个节点的位置,使这些单独存在的空间有了联系。它们在逻辑上就像一条串好的线。一个结点接一个结点。就像火车一样,需要的时候装个车厢,不需要就把车厢撤下来。

顺序表和链表的对比

顺序表:

缺点:

1.申请空间一般按2的倍数申请,但是在使用的时候可能只需要用几个,因此会造成一定程度的空间浪费。

2.每次删除一个数,就要移动这个数后面的其他所有数。删除的数剧靠前,数据总量庞大时,效率太低了。

3.每次内存满了以后就要扩容(realloc),扩容导致性能效率低。

优点:

1.释放内存可以一次性释放。方便。

2.知道一个数的下标,就能访问相邻的两个数。

链表:

缺点:

1.销毁链表时,释放申请的内存属于申请了多少块就要释放多少。

2.单链表只能记住下一个结点的地址,没办法通过当前的地址找到上一个。找上一个结点时要一个个去找。

优点:

1.需要添加一个结点就申请一块空间。不会造成空间浪费。

2.每次删除一个数,只需要改动这个数相邻的两个结点的指针,相对来说影响很小。

3.单链表是申请(malloc)新的空间,不存在扩容。

在实践中,单链表和顺序表都会用,主要看应用场景是什么。

顺序表实现通讯录

1.分析

通讯录的功能有添加联系人,删除联系人,查找联系人,修改联系人,展示通讯录,退出通讯录六个功能。这次需要实现的就六个功能。

建立三个文件。sqcontact.c、sqcontact.h、test.c

sqcontact.h主要放头文件声明,函数声明,宏等。

sqcontact.c主要写函数的具体实现,函数的定义。

test.c主要写通讯录功能的测试,属于测试文件。

2.先写头文件,sqcontact.h

用注释说明了。

头文件里主要写你想要实现哪些功能,就声明哪些函数。先写的时候可以不完整,后面想到再加。

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#pragma once//防止被重复引用
#define NAME_MAX 100
#define SEX_MAX 10
#define TEL_MAX 14
#define ADDR_MAX 100
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>typedef struct personinfo   //联系人是一个结构体,这个结构体里面包含有五个信息。
{char name[NAME_MAX];char sex[SEX_MAX];int age;            //其他信息是数组,因为单个char内存太小不够写,但年龄只需要整型就够char tel[TEL_MAX];char addr[ADDR_MAX];
}personinfo;         //把struct personinfo重命名为personinfotypedef struct seqlist   //整个通讯录是一个结构体,这个结构体里面有联系人数组
{personinfo* info;//这是一个联系人数组,里面有各个联系人信息int capacity;//这个是通讯录的空间大小,可以装多少联系人int size; //这个是通讯录的实际大小,装了多少个联系人
}contact;void Menu();       //菜单的声明  下面是各个功能实现的函数。
//初始化通讯录
void Initcontact(contact* con);//传一级指针是因为要改变con的内容,下面保持接口一致性
//扩容
void Creatcapacity(contact* con);
//添加联系人
void Addcontact(contact* con);
//删除联系人
void Delcontact(contact* con);
//展示联系人
void Showcontact(contact* con);
//查找联系人
void Findcontact(contact* con);
//修改联系人
void Modifycontact(contact* con);
//销毁通讯录
void Destorycontact(contact* con);

2.再写sqcontact.c文件

这里首先要包含头文件的内容。然后去实现头文件的那些函数。

写完一些功能的时候可以测试一下这些代码是否无误。

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include "sqcontact.h"void Menu()
{printf("\n");printf("***************************\n");printf("1.添加联系人\n");printf("2.删除联系人\n");printf("3.修改联系人\n");printf("4.展示联系人\n");printf("5.查找联系人\n");printf("0.退出通讯录\n");printf("***************************\n\n");
}//初始化通讯录
void Initcontact(contact* con)
{con->info = NULL;           //初始化通讯录就是把里面的指针置为NULL,其他赋值。con->capacity = 0;          //也可以把指针赋值,只是为了避免它成为野指针。con->size = 0;
}//扩容
void Creatcapacity(contact* con)   
{if (con->capacity == con->size)//如果容量和实际数据相等(包括0),就需要扩容。{int newcapacity = con->capacity == 0 ? 4 : (2 * con->capacity);personinfo* ptr = (personinfo*)realloc(con->info, sizeof(personinfo) * newcapacity);if (ptr == NULL){perror("realloc failured");exit(1);}con->info = ptr;con->capacity = newcapacity;}//还有容量则什么都不做
}
//尾插
void Addback(contact* con, personinfo* x)
{//一种是直接尾插,一种是头插if (con->size==0)  //头插,因为联系人里没有数据{Creatcapacity(con);//尾插的时候要看一下空间够不够con->info[0] = *x;con->size++;}else//尾插,联系人中有数据{Creatcapacity(con);con->info[con->size] = *x;con->size++;}
}//添加联系人 包含扩容,尾插
void Addcontact(contact* con)
{personinfo x;  //创建一个联系人结构体,填充内容,再把它传给尾插的函数。Creatcapacity(con);   //这句可以不写,后面尾插的时候扩容了的printf("请输入您要添加的联系人姓名:\n");scanf("%s", x.name);   //数组名是首元素地址,所以不用再取地址了printf("请输入您要添加的联系人性别:\n");scanf("%s", x.sex);printf("请输入您要添加的联系人年龄:\n");scanf("%d", &x.age);  //注意,这里要取地址。这个是整型printf("请输入您要添加的联系人电话:\n");scanf("%s", x.tel);printf("请输入您要添加的联系人地址:\n");scanf("%s", x.addr);Addback(con, &x);printf("联系人添加成功!\n");Showcontact(con);//添加成功以后再展示一遍当前通讯录
}//指定位置删除联系人
void Del(contact* con, int pos)
{for (int i = pos; i < con->size-1; i++)//i如果是最后一个,下标为size-1的数,那么{con->info[i] = con->info[i + 1];}con->size--;//通过size--,最后一个数也被删掉了,这个逻辑就完整了。(访问不了等于删掉
}
//删除联系人包含 找联系人 删除联系人
void Delcontact(contact* con)
{assert(con);printf("请输入您要删除的联系人:\n");char name[NAME_MAX];scanf("%s", name);for (int i = 0; i < con->size; i++){if (strcmp(con->info[i].name, name) == 0)//找联系人{//任意位置删除联系人Del(con, i);printf("联系人删除成功\n\n");Showcontact(con);return;}}printf("该联系人不存在!\n");}//展示联系人
void Showcontact(contact* con)
{if (con == NULL){return;}printf("当前联系人如下:\n");printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < con->size; i++){printf("%-4s ", con->info[i].name);printf("%-4s ", con->info[i].sex);printf("%-4d ", con->info[i].age);printf("%-4s ", con->info[i].tel);printf("%-4s ", con->info[i].addr);printf("\n");}
}//查找联系人  这个代码和前面有很多重复部分,找联系人。直接复制之前的代码再修改一下
void Findcontact(contact* con)
{assert(con);printf("请输入您要查找的联系人:\n");char name[NAME_MAX];scanf("%s", name);for (int i = 0; i < con->size; i++){if (strcmp(con->info[i].name, name) == 0){printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");printf("%-4s ", con->info[i].name);printf("%-4s ", con->info[i].sex);printf("%-4d ", con->info[i].age);printf("%-4s ", con->info[i].tel);printf("%-4s ", con->info[i].addr);printf("\n");return;}}printf("该联系人不存在!\n");
}//修改联系人  找联系人,再改联系人。这个代码和前面代码有很多重复部分。直接复制过来再改改
void Modifycontact(contact* con)
{assert(con);printf("请输入您要修改的联系人:\n");char name[NAME_MAX];scanf("%s", name);for (int i = 0; i < con->size; i++){if (strcmp(con->info[i].name, name) == 0){printf("请输入您要修改的联系人姓名:\n");scanf("%s", con->info[i].name);printf("请输入您要修改的联系人性别:\n");scanf("%s", con->info[i].sex);printf("请输入您要修改的联系人年龄:\n");scanf("%d", &con->info[i].age);printf("请输入您要修改的联系人电话:\n");scanf("%s", con->info[i].tel);printf("请输入您要修改的联系人地址:\n");scanf("%s", con->info[i].addr);printf("联系人修改成功!\n");Showcontact(con);return;}}printf("该联系人不存在!\n");
}//销毁通讯录
void Destorycontact(contact* con)
{assert(con);  //不能释放已经释放的动态内存free(con->info); //释放内存空间con->info = NULL;//再把指针置为空con->size = con->capacity = 0;//其他变为0
}

3.写test.c文件

可以先实现一部分功能以后就写test.c文件测试一下那些功能是否能顺利运行。

首先还是包含一个头文件。初始化和创建通讯录不能在do while循环里。测试文件相比比较简单。

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include "sqcontact.h"int main()
{contact s1;Initcontact(&s1);int num = 0;do{Menu();printf("请输入您要进行的操作:\n");scanf("%d", &num);switch (num){case 1: Addcontact(&s1);break;case 2: Delcontact(&s1);break;case 3: Modifycontact(&s1);break;case 4: Showcontact(&s1);break;case 5: Findcontact(&s1);break;case 0: Destorycontact(&s1);break;default: printf("输入错误,请重新输入!\n");break;}} while (num);return 0;
}

小提示

写代码的时候第一次运行发现报错很多也别慌,看报错的内容一个个找原因就好。

主要是逻辑上没有错误,没有遗漏情况,没有数组越界,没有释放空指针等。然后是代码有没有问题,函数名是否写错,重复定义,漏写头文件,取地址,类型错误等等。

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

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

相关文章

[C语言]结构体初识

结构体定义 结构体是一些值的集合,被成为成员变量,结构的每个成员可以是不同类型的变量 声明: 定义了一个结构体比如以张蓝图,不占据内存,当你创建了一个结构体变量时,才占空间. #include<stdio.h>//struct 为结构体关键字, student 自定义结构体名称 struct student …

Markdown 图片尺寸对齐等详细使用

✍️作者简介&#xff1a;小北编程&#xff08;专注于HarmonyOS、Android、Java、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您些帮助请&#x1f449;关…

全链路压测的关键点是什么?

全链路压测是一种重要的性能测试方法&#xff0c;用于评估应用程序或系统在真实生产环境下的性能表现。通过模拟真实用户行为和流量&#xff0c;全链路压测能够全面评估系统在不同负载下的稳定性和性能表现。本文将介绍全链路压测的关键点&#xff0c;以帮助企业更好地理解和应…

【第二十二课】最短路:dijkstra算法 ( acwing849 / acwing850 / c++ 代码)

目录 dijkstra算法求最短距离步骤 朴素的dijkstra算法---acwing-849 代码如下 代码思路 堆优化版的dijkstra算法---acwing-850 代码如下 关于最短路问题分有好几种类型 &#xff1a; 单源就是指&#xff1a;只求从一个顶点到其他各顶点 多源是指&#xff1a;要求每个顶…

SD-WAN和MPLS的区别以及如何选择?

网络连接技术的选择对企业来说至关重要。SD-WAN&#xff08;软件定义广域网&#xff09;和MPLS&#xff08;多协议标签交换&#xff09;是两种备受关注的网络连接方案。它们在架构、带宽、成本和管理等方面存在显著区别&#xff0c;企业应了解清楚这些区别再进行选择。 SD-WAN采…

AI算力专题:从超微电脑创新高看AI算力产业链高景气

今天分享的是AI算力系列深度研究报告&#xff1a;《AI算力专题&#xff1a;从超微电脑创新高看AI算力产业链高景气》。 &#xff08;报告出品方&#xff1a;太平洋证券&#xff09; 报告共计&#xff1a;10页 海外巨头指引 Al 算力产业链高景气 超微电脑业绩指引大幅上调反映…

三子棋游戏小课堂

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今天的主菜是&#xff0c;C语言实现的三子棋小游戏&#xff0c; 所属专栏&#xff1a; C语言知识点 主厨的主页&#xff1a;Chef‘s blog 前言&…

机器学习 | 掌握逻辑回归在实践中的应用

目录 初识逻辑回归 逻辑回归实操 分类评估方法 初识逻辑回归 逻辑回归&#xff08;LogisticRegression&#xff09;是机器学习中的一种分类模型&#xff0c;逻辑回归是一种分类算法&#xff0c;虽然名字中带有回归&#xff0c;但是它与回归之间有一定的联系。由于算法的简单…

【Spark系列2】Spark编程模型RDD

RDD概述 RDD最初的概述来源于一片论文-伯克利实验室的Resilient Distributed Datasets&#xff1a;A Fault-Tolerant Abstraction for In-Memory Cluster Computing。这篇论文奠定了RDD基本功能的思想 RDD实际为Resilient Distribution Datasets的简称&#xff0c;意为弹性分…

【大厂AI课学习笔记】1.3 人工智能产业发展(2)

&#xff08;注&#xff1a;腾讯AI课学习笔记。&#xff09; 1.3.1 需求侧 转型需求&#xff1a;人口红利转化为创新红利。 场景丰富&#xff1a;超大规模且多样的应用场景。主要是我们的场景大&#xff0c;数据资源丰富。 抗疫加速&#xff1a;疫情常态化&#xff0c;催生新…

Windows11通过Hyper-V创建VM,然后通过vscode连接vm进行开发

这边需要在win11上建立vm来部署docker(这边不能用windows版本的docker destop)&#xff0c;学习了下&#xff0c;记录。 下载系统镜像 首先下载系统镜像&#xff1a;https://releases.ubuntu.com/focal/ 这边使用的是ubuntu20.04.6 LTS (Focal Fossa) &#xff0c;Server inst…

1484. 按日期分组销售产品

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 表 Activities&#xff1a; ---------------------- | 列名 | 类型 | --------…

CIFAR-10数据集详析:使用卷积神经网络训练图像分类模型

1.数据集介绍 CIFAR-10 数据集由 10 个类的 60000 张 32x32 彩色图像组成&#xff0c;每类 6000 张图像。有 50000 张训练图像和 10000 张测试图像。 数据集分为5个训练批次和1个测试批次&#xff0c;每个批次有10000张图像。测试批次正好包含从每个类中随机选择的 1000 张图像…

易优CMS采集插件使用教程

本易优CMS采集教程说明如何使用易优CMS采集插件&#xff0c;批量获取互联网上的文章数据&#xff0c;并自动更新到易优cms&#xff08;eyoucms&#xff09;网站&#xff0c;快速丰富网站的内容。 目录 1. 下载并安装易优CMS采集插件 2. 对接网页文章采集工具 3. 采集数据发…

GPT-4级别模型惨遭泄露!引爆AI社区,“欧洲版OpenAI”下场认领

大家好&#xff0c;我是二狗。 这两天&#xff0c;一款性能接近GPT-4的模型惨遭泄露&#xff0c;引发了AI社区的热议。 这背后究竟是怎么回事呢&#xff1f; 起因是1月28日&#xff0c;一位名为“Miqu Dev”的用户在 HuggingFace 上发布了一组文件&#xff0c;这些文件共同组…

Steam爆火游戏幻兽帕鲁自建多人联机专用服务器配置要求

《幻兽帕鲁》这款多人游戏模式的全新开放世界生存制作游戏&#xff0c;在短短上线5天就卖出700万份&#xff0c;同时在线人数最高达到了180万人&#xff0c;创下Steam历史榜单第二名的好成绩&#xff0c;意料之外的爆火也一度导致幻兽帕鲁出现无法创建4人游戏房间、官方服务器连…

C语言-算法-最短路

【模板】Floyd 题目描述 给出一张由 n n n 个点 m m m 条边组成的无向图。 求出所有点对 ( i , j ) (i,j) (i,j) 之间的最短路径。 输入格式 第一行为两个整数 n , m n,m n,m&#xff0c;分别代表点的个数和边的条数。 接下来 m m m 行&#xff0c;每行三个整数 u …

VUE3:组合式API生命周期

1、onMounted 注册一个回调函数&#xff0c;在组件挂载完成后执行。 组件在以下情况下被视为已挂载&#xff1a; – 1. 其所有同步子组件都已经被挂载。 – 2. 其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时&#xff0c;才可以保证组件 DOM 树也在文…

已定式,未定式【高数笔记】

【已定式】 将x-->? 的过程代入到lim中&#xff0c;如果得出的结果可以判断出&#xff0c;lim是有极限的&#xff0c;则为已定式 [举例] lim(1/x)&#xff0c;x--> 无穷 &#xff0c;即&#xff0c;1/ 无穷 0 &#xff0c;所以为已定式 【未定式】 将x-->? 的过程代…

docker 搭建 Seafile 集成 onlyoffice

docker-compose一键部署yaml文件 version: "3"services:db:image: mariadb:10.11container_name: seafile-mysqlenvironment:- MYSQL_ROOT_PASSWORDdb_dev # Requested, set the roots password of MySQL service.- MYSQL_LOG_CONSOLEtruevolumes:- /share/ZFS18_D…