前言:
小编在前文写了关于顺序表的内容,但是顺序表并不局限于小编写的那个,顺序表的应用是很广泛的,下面小编开始讲述顺序表的应用,顺序表的实现。
目录 :
1.通讯录的逻辑实现
1.1通讯录是什么
1.2.通讯录和顺序表的关系
1.2.通讯录的逻辑实现
2.通讯录的代码实现
3.代码的优化
4.代码展示
正文:
1.通讯录的逻辑实现
1.1.通讯录是什么
这个其实很好进行解释,我们平常在手机上存放别人信息的目录,就是通讯录,所以通讯录通常记录着联系人的姓名,性别,年龄,电话,家庭住址等等。这便是通讯录所包含的内容。下面小编先来展示一下对于通讯录结构体的创建:
#define Max_name 20
#define Max_gender 10
#define Max_tel 20
#define Max_pla 30 //这里使用宏是方便后期如果需要更大的数可以直接通过宏来实现改变
typedef struct tongxunlu
{
char name[Max_name]; //姓名
char gender[Max_gender]; //性别
int age; //年龄
char tel[Max_tel]; //电话
char pla[Max_pla]; //家庭住址
}PeoInfo;
1.2.通讯录和顺序表的关系
小编在前面说过,通讯录是顺序表的一个应用,所以其实我们想要实现通讯录,就必须以顺序表作为基础,所以顺序表是通讯录实现的底层代码,读者朋友们一定要先掌握好顺序表的内容在搞通讯录,不要一口吃个小编。
1.3.通讯录的逻辑实现
和顺序表一样,通讯录想要实现也需要创建两个文件,一个是存放通讯录的创建以及部分函数声明的头文件,一个是实现函数的.c文件(源文件)。下面是展示所有文件的图片:
首先,我们先要创建一个通讯录,根据小编前面对通讯录的介绍,通讯录里面存放着很多信息,那么此时,那个能存放很多类型的数据的变量来喽,此时我们要用结构体来存储这些数据,此时是完成了对于此结构体的实现,我们在创立完成以后,便可以开始对通讯录进行初始化了
小编在刚刚说出哦,通讯录的实现的底层代码是顺序表,所以我们可以完全通过前文的顺序表的代码,来基于此开展最详细的代码书写,不过此时我们要更改一些数据,就比如前文我们讲过的顺序表的变量都是整型,这次要改成结构体类型了(通讯表的类型),此时就要展现小编前文写过的对于typedef的妙用了,这个时候我们直接把整型改成通讯录的结构体类型就好了,这里就展示了此代码的灵活性,不过我们想要运用通讯录的时候,一定要记得包含通讯录的头文件,不然会报错的。之后我们再利用顺序表的初始化在对通讯表进行初始化,销毁的操作也是一样的,小编就不一一废话了。
对于通讯录一些功能的实现,无非就是添加联系人,删除联系人,更改联系人,查找联系人,展示联系人罢了,下面我们对这几个函数实现的逻辑一一叙述。
下面先来讲添加联系人函数的逻辑实现,这个是基于顺序表中的插入函数(头插,尾插,指定位置插)来进行描写的,不过比那个要复杂很多,此时我们需要创建一个通讯录变量,来对这个变量的内容通过scanf语句来进行输入,确保将姓名,电话,地址等等数据填上,之后我们可以把这个写完的数据,通过顺序表的任意一个插入功能来实现,此时我们可以用任何一个插入函数来进行,小编在后续代码中使用的是尾插操作来进行的,此时是为了把我们的数据放入结构体数组之中。
此时为了让之后文章语句以及代码变得更容易可读,小编将原顺序表设置好的内容更名为通讯录了(这里也运用到typedef关键字,可以看出它应用的广泛),所以通讯录中是存放着结构体类型的数组,有效个数,总空间个数。
之后我们要进行删除联系人的操作,此时我们在进行操作之前,要先确保通讯录是不为空的,也就是通讯录中是有元素存在的,此时我们需要用到assert断言(此断言小编在之前的文章提到过),来判断结构体数组是否为空,在进行完判断以后,就要准备进行我们的删除操作了,首先,我们想要删除联系人的时候,底层逻辑肯定是顺序表的删除操作,因为我们删除的联系人是指定的,所以我们要使用指定位置删除的函数,此时我们需要设置一个字符数组,我们平常在删除别人的时候,肯定要先知道这个人的名字,所以字符数组来存放着我们想要删除人的姓名,然后我们遍历数组,看看名字是否对应着数组中的元素,如果对应就调用底层代码中的删除指定位置的函数,从而完成删除联系人的操作。
在之后我们要进行更改联系人的操作,此操作并没有对应着顺序表的函数,所以是一个可以看为是一个全新的函数,此时我们前面的操作和删除联系人操作是一样的,先看看指定联系人存不存在,然后遍历整个数组,存在就返回坐标(这里小编单独分出了一个函数,下面代码实现小编会提到的,放心),然后如果找到了,就把指定位置的数组内容给改掉。是不是感觉到:
下面我们来简略介绍下查找联系人,这个更是和删除联系人的操作一样,是不是感觉越来越简单了,小编在上篇文章就说过万事开头难,我们在学完顺序表以后再来弄应用,就会发现难度没有想象中的那么大了,所以读者朋友做事时不要因为一点小困难就放弃哦~不扯远了,我们在遍历完数组后,如果找到了就打印找到了,然后把指定数据打印完就好,如果没有找到就显示没有找到。
最后我们来讲一下展示通讯录的操作,不要被它的名字吓到,如果用大白话来讲,其实这就是打印数组的操作,具体操作小编就在代码部分展示了,这里没什么好说的,那么下面进入我们的代码页喽!
2.通讯录的代码实现(读者朋友每次写完一个函数记得调试试一下)
首先先来我们的初始化操作,这里其实套用到就是顺序表的初始化,不过此时我们已经对顺序表的名字进行改名了,这里是在contact.h文件进行改变的,所以我们要使用前置声明(因为此时顺序表的头文件并没有包含在通讯录的头文件中,大家千万不要文件进行嵌套(就是两个头文件相互包含对方,这样是万万不可的,代码会出错的)),下面直接上初始化的代码:
void InitContact(contact* con) { //这里我们可以直接套用顺序表的初始化 SLInit(con); }
typedef PeoInfo SlDateType; //先设置动态链表,静态链表局限性太高了,所以不推荐使用 typedef struct Sqllist { SlDateType * arr; //数组大小未知,所以先不用写大小 int size; //有效数据个数 int kongjian; //表示总的大小 }SL;//弄个顺序表的名字,咱就叫SL了
void SLInit(SL* ps) { ps->arr = NULL; ps->size = ps->kongjian = 0; //让有效个数和总的个数都为0 }
小编也把上篇文章的顺序表初始化代码展现出来了,避免部分读者朋友可能没有看上一篇文章,然后我们还是老规矩,先进行销毁操作,我们既然开辟了空间,一定要记着回收,养成好的代码习惯,避免以后出现许多的代码错误,下面直接来展示销毁代码的撰写:
void DestroyContact(contact* con) //销毁操作 { //直接用顺序表的销毁操作就好了 SLDestroy(con); }
void SLDestroy(SL* ps) { if (ps->arr) { ps->arr = NULL; } ps->size = ps->kongjian = 0; //让我们熟悉的个数继续变成0 }
下面直接进入主要撰写函数环节,这里我们先来进行添加联系人的操作,这里我们在前面逻辑说了,先用scanf语句来把该有的给打印出来,这里我们输入完数据以后直接把它套在尾插函数就好,下面是代码展示:
void AddContact(contact* 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.pla); //现在我们要把数据插入到我们的顺序表中,下面可以直接引用顺序表的代码 SLPushBack(con, info); //此时已经插入数据了,可以调试看一看 }
void SLPushBack(SL* ps, SlDateType x) { assert(ps); //先判断有效个数和空间大小之间的关系,如果相等了,我们就做出扩容的处理 daxiaoguanxi(ps); ps->arr[ps->size++] = x; }
然后我们在进行删除联系人的操作,小编在前面说过,我们在这里需要先设置好一个数组,来存放我们自己写的名字,然后遍历数组,来看看数据是否存在,如果存在的化,我们就要进行删除操作,不存在的话,打印出函数不存在这句话,我们可以让负数作为返回值(因为数组的下标肯定是大于等于0的,如果遍历完还没有的话就返回负数) ,从而可以判断是否存在相应的数据,下面来进行代码的展示:
这个是遍历数组来看看数据是否存在的函数:
int panduan(contact* con,char* name) { int i = 0; for (i = 0; i < con->size; i++) { if (strcmp(name, con->arr[i].name) == 0) { return i; } } return -1; }
删除联系人函数:
void DelContact(contact* con) { char name[Max_name]; //删除操作,要用指定位置进行删除的顺序表代码 printf("请输入您想要删除的联系人姓名:\n"); scanf("%s", name); int m =panduan(con,name); if (m < 0) { printf("没有找到想要的联系人!\n"); return; } printf("叮~,删除成功!\n"); SLErase(con, m); }
void SLErase(SL* ps, int pos) { assert(ps && ps->size && ps->kongjian); int i = 0; for (i = pos;i < ps -> size - 1 ; i++) { ps->arr[i] = ps->arr[i + 1]; //ps -> arr[size - 2] = ps -> arr[size - 1] } ps->size--; }
下面我们来进行更改联系人的操作,此时我们依旧用到了我们的判断函数,下面放置代码的时候小编就不多放了,然后我们在进行相同的判断操作,上面的判断操作判断成功是进行删除操作,这里我们则是把对应的数据在次用scanf来进行替换,下面是代码展示:
void ModifyContact(contact* con) { char name[Max_name]; printf("请选择您想要修改联系人的姓名 : \n"); scanf("%s", name); int m = panduan(con, name); if (m < 0) { printf("您想要修改的联系人姓名不存在!\n"); return; } printf("请输入姓名:\n"); scanf("%s", con -> arr[m].name); printf("请输入性别:\n"); scanf("%s", con->arr[m].gender); printf("请输入年龄:\n"); scanf("%d", &con->arr[m].age); printf("请输入电话:\n"); scanf("%s",con -> arr[m].tel); printf("请输入地址:\n"); scanf("%s", con->arr[m].pla); printf("叮~修改成功!"); }
然后我们在进行查找联系人操作,这个操作和上面的代码又是如出一辙,不过这次我们找到后是把数据打印出来,所以我们就用printf函数来代替scanf函数,下面上代码:
void FindContact(contact* con) { char name[Max_name]; printf("请输入您要查找的联系人的姓名: \n"); scanf("%s", name); int m = panduan(con, name); if (m < 0) { printf("您想要查找的联系人不存在!\n"); return 1; } printf("您想要查找到数据存在!,下面是您查找到数据 : \n"); printf("%-10s %-4s %-4d %15s %-20s", con->arr[m].name, con->arr[m].gender, con->arr[m].age, con->arr[m].tel, con->arr[m].pla ); printf("\n"); }
最后我们来进行展示通讯录的操作,前面小编说了,其实这就是打印通讯录的操作,所以我们可以通过循环的方式来打印数组内容,在巧妙的用->和.操作符(我准备单独出一节结构体的文章,坏了,我似乎欠了好多文章)进行操作,下面直接上代码展示:
void ShowContact(contact* con) //展示通讯录的项目 { printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址"); int i = 0; for (i = 0; i < con->size; i++) { printf("%-10s %-4s %-4d %15s %-20s", con->arr[i].name, con->arr[i].gender, con->arr[i].age, con->arr[i].tel, con->arr[i].pla ); printf("\n"); } }
以上便是这个代码的书写过程,希望读者朋友们好好掌握这部分的内容,并且一定要领悟了顺序表在写通讯录这一个简单的项目,可能很多读者朋友们会觉得作为一个项目,我这么写,没有应用过程是不算一个好的项目的,别急,小编特意出了一个代码优化过程,此过程会让这个代码变得很完整,小编不卖关子了,下面进入本文的下一项:
3.代码的优化
这个优化环节其实和我之前写的扫雷游戏很像,其实我的优化就是加了个菜单页,下面先放上我写的菜单页:
void menu() { printf("************************通讯录************************************\n"); printf("*******1.添加联系人*************2.删除联系人**********************\n"); printf("*******3.查找联系人*************4.展示联系人**********************\n"); printf("*******0.退出*****************************************************\n"); printf("******************************************************************\n"); }
是不是有很熟悉的感觉,小编之前已经写了不少的菜单页了,之后我们就要开始选择这些内容了,此时我们可以先输入一个数,然后用小编之前写过的分支语句文章中的,我们的老朋友switch语句,这个语句是来判断我们输入的数的,此时为了我们能一直保持在通讯录页面,我们可以选择老三do while语句来进行判断循环,因为这个循环的特点是,先循环一次在判断再判断条件,下面不多废话,直接展示成品:
int main() { int i = 0; contact ps; InitContact(&ps); do { menu(); printf("请选择您想要的操作:\n"); scanf("%d", &i); switch (i) { case 1: AddContact(&ps); break; case 2: DelContact(&ps); break; case 3: FindContact(&ps); break; case 4: ShowContact(&ps); break; case 0: default: printf("您选错了,请重新选择\n"); break; } } while (i); DestroyContact(&ps); return 0; }
此时就是这个代码完全体了,此时我们可以通过输入一个数来实验这个函数(为了不让文章太过复杂小编就不展示了,读者朋友们可以自行调试一下) ,可能许多读者朋友想要知道完整的代码,因为小编讲的不够细,不要急,下面来进行展示代码环节:
4.代码展示:
test.h
#define _CRT_SECURE_NO_WARNINGS #include"Seqlist.h" #include"Contact.h" void menu() { printf("************************通讯录************************************\n"); printf("*******1.添加联系人*************2.删除联系人**********************\n"); printf("*******3.查找联系人*************4.展示联系人**********************\n"); printf("*******0.退出*****************************************************\n"); printf("******************************************************************\n"); } int main() { int i = 0; contact ps; InitContact(&ps); do { menu(); printf("请选择您想要的操作:\n"); scanf("%d", &i); switch (i) { case 1: AddContact(&ps); break; case 2: DelContact(&ps); break; case 3: FindContact(&ps); break; case 4: ShowContact(&ps); break; case 0: default: printf("您选错了,请重新选择\n"); break; } } while (i); DestroyContact(&ps); return 0; }
Seqlist.h
#pragma once #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include"Contact.h" //开始通讯录操作了 typedef PeoInfo SlDateType; //先设置动态链表,静态链表局限性太高了,所以不推荐使用 typedef struct Sqllist { SlDateType * arr; //数组大小未知,所以先不用写大小 int size; //有效数据个数 int kongjian; //表示总的大小 }SL;//弄个顺序表的名字,咱就叫SL了 void print(ps); //我们先初始化顺序表 void SLInit(SL* ps); //初始化函数 void SLDestroy(SL* ps); //销毁函数,这个大可以等会再用,但是先写上,防止等会函数变多了,就顺理成章的忘记了我们的销魂函数了 void SLPushBack(SL* ps, SlDateType x); //定义一个尾插函数,实现我们的尾插功能 void SLPushFront(SL* ps, SlDateType x); //这个是头插 void SLPopBack(SL* ps); //这个执行的是尾删的操作 void SLPopFront(SL* ps); //这个是头删的操作 void SLInsert(SL* ps, int pos, SlDateType x); //指定位置进行插入数据 void SLErase(SL* ps, int pos); //这个进入指定位置删除的操作 int SLFind(SL* ps, SlDateType x); //这个是指定元素进行查找的操作
Seqlist.c
#define _CRT_SECURE_NO_WARNINGS #include"Seqlist.h" void SLInit(SL* ps) { ps->arr = NULL; ps->size = ps->kongjian = 0; //让有效个数和总的个数都为0 } void SLDestroy(SL* ps) { if (ps->arr) { ps->arr = NULL; } ps->size = ps->kongjian = 0; //让我们熟悉的个数继续变成0 } void daxiaoguanxi(SL* ps) { if (ps->size == ps->kongjian) { int newkongjian = ps->kongjian == 0 ? 4 : (ps->kongjian * 2); //扩容是呈现倍数的增长,所以先乘上2 SlDateType* arr1 = (SlDateType*)realloc(ps->arr, newkongjian * sizeof(SlDateType)); assert(arr1); ps->arr = arr1; ps->kongjian = newkongjian; //行了熬不住了先睡觉了明天接着肝,明天记者维护一下git,这里应该是属于数据结构了,所以建立一个数据结构仓库 } } void SLPushBack(SL* ps, SlDateType x) { assert(ps); //先判断有效个数和空间大小之间的关系,如果相等了,我们就做出扩容的处理 daxiaoguanxi(ps); ps->arr[ps->size++] = x; } void SLPushFront(SL* ps, SlDateType x) { int i = 0; for (i = ps->size; i > 0; i--) { ps->arr[i] = ps->arr[i - 1]; //现在的我就是一个蠢蛋,服了,睡个交起来脑子直接变得更不行了,我得反思了,醉了 } ps -> arr[0] = x; ps -> size++; } void SLPopBack(SL* ps) { assert(ps -> size); ps->size--; } void SLPopFront(SL* ps) { int i = 0; assert(ps -> size); //看有效个数判断是否还能继续进行删除 for (i = 1; i <= ps -> size - 1; i++) { ps->arr[i - 1] = ps->arr[i]; //arr[size -2] = arr[size - 1]; } ps->size--; } void SLInsert(SL* ps, int pos, SlDateType x) { assert(ps); assert(ps->size); assert(ps->kongjian); daxiaoguanxi(ps); int i = 0; for (i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[pos] = x; ps -> size++; } void SLErase(SL* ps, int pos) { assert(ps && ps->size && ps->kongjian); int i = 0; for (i = pos;i < ps -> size - 1 ; i++) { ps->arr[i] = ps->arr[i + 1]; //ps -> arr[size - 2] = ps -> arr[size - 1] } ps->size--; }
Contact.h
#pragma once #include<stdio.h> #define Max_name 20 #define Max_gender 10 #define Max_tel 20 #define Max_pla 30 typedef struct Sqllist contact; //前置声明 typedef struct tongxunlu { char name[Max_name]; //姓名 char gender[Max_gender]; //性别 int age; //年龄 char tel[Max_tel]; //电话 char pla[Max_pla]; //家庭住址 }PeoInfo; //先给顺序表改一个名字 //得先初始化 void InitContact(contact* con); //先初始化顺序表s //添加通讯录数据 void AddContact(contact* con); void DestroyContact(contact* con); //销毁通讯录项目 void DelContact(contact* con); //删除通讯录的项目 void ShowContact(contact* con); //这个是展示通讯录的操作,其实说白了就是打印顺序表的内容 void FindContact(contact* con); //这个是查找通讯录 void ModifyContact(contact* con); //这个是修改通讯录
Contact.c
#define _CRT_SECURE_NO_WARNINGS #include"Seqlist.h" #include"Contact.h" void InitContact(contact* con) { //这里我们可以直接套用顺序表的初始化 SLInit(con); } void AddContact(contact* 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.pla); //现在我们要把数据插入到我们的顺序表中,下面可以直接引用顺序表的代码 SLPushBack(con, info); //此时已经插入数据了,可以调试看一看 } void DestroyContact(contact* con) //销毁操作 { //直接用顺序表的销毁操作就好了 SLDestroy(con); } int panduan(contact* con,char* name) { int i = 0; for (i = 0; i < con->size; i++) { if (strcmp(name, con->arr[i].name) == 0) { return i; } } return -1; } void DelContact(contact* con) { char name[Max_name]; //删除操作,要用指定位置进行删除的顺序表代码 printf("请输入您想要删除的联系人姓名:\n"); scanf("%s", name); int m =panduan(con,name); if (m < 0) { printf("没有找到想要的联系人!\n"); return; } printf("叮~,删除成功!\n"); SLErase(con, m); } void ShowContact(contact* con) //展示通讯录的项目 { printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址"); int i = 0; for (i = 0; i < con->size; i++) { printf("%-10s %-4s %-4d %15s %-20s", con->arr[i].name, con->arr[i].gender, con->arr[i].age, con->arr[i].tel, con->arr[i].pla ); printf("\n"); } } void FindContact(contact* con) { char name[Max_name]; printf("请输入您要查找的联系人的姓名: \n"); scanf("%s", name); int m = panduan(con, name); if (m < 0) { printf("您想要查找的联系人不存在!\n"); return 1; } printf("您想要查找到数据存在!,下面是您查找到数据 : \n"); printf("%-10s %-4s %-4d %15s %-20s", con->arr[m].name, con->arr[m].gender, con->arr[m].age, con->arr[m].tel, con->arr[m].pla ); printf("\n"); } void ModifyContact(contact* con) { char name[Max_name]; printf("请选择您想要修改联系人的姓名 : \n"); scanf("%s", name); int m = panduan(con, name); if (m < 0) { printf("您想要修改的联系人姓名不存在!\n"); return; } printf("请输入姓名:\n"); scanf("%s", con -> arr[m].name); printf("请输入性别:\n"); scanf("%s", con->arr[m].gender); printf("请输入年龄:\n"); scanf("%d", &con->arr[m].age); printf("请输入电话:\n"); scanf("%s",con -> arr[m].tel); printf("请输入地址:\n"); scanf("%s", con->arr[m].pla); printf("叮~修改成功!"); }
总结:
这篇文章小编到这里也是写完了,这篇文章其实前天就开始写了,只不过小编最近在期末复习(其实单纯懒)没有写,今天闲来无事遂写篇文章督促自己,整体这么堕落怎么才能成为好的码农,好了,不多废话了,如果文章有错误,请大家指出,小编肯定会聆听各位的反馈,那么,我们下篇文章再见喽!