前言
数据结构(初阶)第一节:数据结构概论-CSDN博客
数据结构(初阶)第二节:顺序表-CSDN博客
本文将以C语言和顺序表实现通讯录基础管理,实现功能包括增、删、改、查等,在实现相关功能时需要用到在第二节中顺序表的相关内容,需要友友们掌握顺序表的相关内容以及函数的实现方式。
目录
前言
要用到的两个文件
正文
文件包含关系
Contact.h
Contast.c
头文件
菜单
初始化
销毁
添加
判断名字是否存在
删除
显示
修改
查找
测试文件test.c
要用到的两个文件
SeqList.h
//.h文件定义#include "Contact.h"//头文件互相包含会报错#include <malloc.h>#include <assert.h>#include <string.h>#include <stdio.h>typedef peoInfo SLDataType;//定义顺序表typedef struct SeqList{ SLDataType* a;//数组 int size;//有效元素 int capacity;//容量}SL;//初始化void SLinit(SL* p1);//销毁void SLdestory(SL* p1);//扩容void SLcheckCapcity(SL* p1);//尾插void SLpushBack(SL* p1, SLDataType x);//打印顺序表void SLprint(SL* p1);//头插void SLpushFront(SL* p1, SLDataType x);//尾删void SLpopBack(SL* p1);//头删void SLpopFront(SL* p1);//指定插入void SLinsert(SL* p1, int pos, SLDataType x);//指定删除void SLerase(SL* p1, int pos);//查询//int SLfind(SL* p1, SLDataType x);
SeqList.c
#include "SeqList.h"//初始化void SLinit(SL* p1){ p1->a = (SLDataType*)malloc((sizeof(SLDataType)) * 4); if (p1->a == NULL) { perror("malloc fail"); return; } p1->capacity = 4; p1->size = 0;}//销毁void SLdestory(SL* p1){ free(p1->a); p1->a = NULL; p1->capacity = 0; p1->size = 0;}//扩容void SLcheckCapcity(SL* p1){ if (p1->size >= p1->capacity) { SLDataType* tmp = (SLDataType*)realloc(p1->a, sizeof(SLDataType) * p1->capacity * 2); if (tmp == NULL) { perror("realloc fail"); return; } p1->a = tmp; p1->capacity *= 2; }}//尾插void SLpushBack(SL* p1, SLDataType x){ assert(p1); SLcheckCapcity(p1);//检查是否需要扩容 p1->a[(p1->size)++] = x;//在size处插入数据}//打印顺序表void SLprint(SL* p1){ for (int i = 0; i < p1->size; i++) { printf("%d\n", p1->a[i]); }}//头插void SLpushFront(SL* p1, SLDataType x){ assert(p1); SLcheckCapcity(p1); for (int i = p1->size; i > 0; i--) { p1->a[i] = p1->a[i - 1]; } p1->a[0] = x; p1->size++;}//尾删void SLpopBack(SL* p1){ assert(p1); assert(p1->size);//顺序表不为空 //p1->a[p1->size - 1] = -1; p1->size--;}//头删void SLpopFront(SL* p1){ assert(p1); assert(p1->size); for (int i = 1; i < p1->size; i++) { p1->a[i - 1] = p1->a[i]; } p1->size--;}//指定下标添加void SLinsert(SL* p1, int pos, SLDataType x){ //要注意p1->size指向的是最后一个有效数据的下一位 //pos是指定的插入位置的下标(如果为0则是头插,如果为ps->size-1则为尾插) //x是待插入的数据 assert(p1 && pos >= 0 && pos < p1->size); SLcheckCapcity(p1); for (int i = p1->size; i > pos; i--) { p1->a[i] = p1->a[i - 1]; } p1->a[pos] = x; p1->size++;}//指定下标删除void SLerase(SL* p1, int pos){ assert(p1 && pos >= 0 && pos < p1->size); for (int i = pos; i < p1->size - 1; i++) { p1->a[i] = p1->a[i + 1]; } p1->size--;}//查询//int SLfind(SL* p1, SLDataType x)//{ // assert(p1);// for (int i = 0; i < p1->size; i++)// { // if (p1->a[i] == x)// { // return i;//找到后返回下标// }// }// return -1;//没有找到返回-1//}
正文
文件包含关系
在实现通讯录的工程文件中一共包含了5个子文件,分别是
- test.c:用于在编写过程中测试代码能否正常运行
- SeqList.h:用于在实现顺序表的过程中定义结构体和各种方法
- SeqList.c:用于实现在头文件中定义的方法
- Contact.h:定义通讯录中实现功能的函数
- Contact.c:实现头文件中定义的函数
通讯录实质上就是顺序表,只不过是改了名字(换汤不换药),我们只需要在实现顺序表的基础上给他起个别名通讯录(Contact)即可。
在顺序表中,数组中存储的是单一的元素,在通讯录中,原数组中的元素变成了存储联系人数据的结构体(personInfo),数组中的每个元素都是结构体类型,包括姓名、电话、性别、住址等,本质上是两个结构体的嵌套。
在Contact.h中我们定义好联系人结构体和要用到的方法
#define NAME_MAX 20#define GENDER_MAX 5#define PHONE_MAX 20#define ADDS_MAX 20typedef struct personInfo{ char name[NAME_MAX]; char gender[GENDER_MAX]; int age; char phoneNum[PHONE_MAX]; char adds[ADDS_MAX];}peoInfo;//前置声明typedef struct SeqList Contact;//将顺序表命名为"通讯录"//菜单void menu();//初始化void ContactInit(Contact* p);//销毁void ContactDestory(Contact* p);//添加void ContactAdd(Contact* p);//删除void ContactDle(Contact* p);//修改void ContactModify(Contact* p);//查找void ContactFind(Contact* p);//显示void ContactShow(Contact* p);
typedef struct SeqList Contact;在这一句代码中使用前置声明将Seqlist重命名为Contact,在该文件中我们并没有定义结构体SeqList,使用前置声明只是为了让编译器知道有这个结构体的存在,而无法直接对之前重命名过的SL(typedef struct SeqList SL;)再命名的原因是编译器不能识别到SL的存在,如果想要识别必须包含"SeqList.h",但是头文件相互包含会导致报错,后面会讲到。
在SeqList.h中将SLDateType自定义类型更改为perInfo,需要将"Contact.h"包含进文件,不能将"SeqList.h"同时包含进Contact.h中,这样会导致程序报错。
typedef peoInfo SLDataType;//定义顺序表typedef struct SeqList{ SLDataType* a;//数组 int size;//有效元素 int capacity;//容量}SL;
在Contact.h中对SeqList重命名,在SeqList.h中更改自定义数据类型,此时我们通过Contact*p和SL*p定义的两种结构体指针都会被程序正确识别,本质上Contact*p等价于SL*p。
#define NAME_MAX 20#define GENDER_MAX 5#define PHONE_MAX 20#define ADDS_MAX 20typedef struct personInfo{ char name[NAME_MAX]; char gender[GENDER_MAX]; int age; char phoneNum[PHONE_MAX]; char adds[ADDS_MAX];}peoInfo;//前置声明typedef struct SeqList Contact;//将顺序表命名为"通讯录"//菜单void menu();//初始化void ContactInit(Contact* p);//销毁void ContactDestory(Contact* p);//添加void ContactAdd(Contact* p);//删除void ContactDle(Contact* p);//修改void ContactModify(Contact* p);//查找void ContactFind(Contact* p);//显示void ContactShow(Contact* p);
Contast.c
头文件
#include "Contact.h"#include "SeqList.h"
菜单
void menu(){ printf("-----------------------\n"); printf(" 1.添加 \n"); printf(" 2.删除 \n"); printf(" 3.查找 \n"); printf(" 4.显示 \n"); printf(" 5.修改 \n"); printf(" 0.退出 \n"); printf("-----------------------\n");}
初始化
//初始化void ContactInit(Contact* p){ SLinit(p);//直接调用已经在SeqList.c中实现好的初始化函数即可}
销毁
void ContactDestory(Contact* p){ SLdestory(p);}
添加
void ContactAdd(Contact* p){ 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.phoneNum); printf("请输入联系人的住址:\n"); scanf("%s", info.adds); SLpushBack(p, info);//这里选择尾插 printf("添加成功!\n\n");}
判断名字是否存在
int FindName(Contact* p, char* name){ for (int i = 0; i < p->size; i++) { if (strcmp(p->a[i].name, name) == 0) return i;//返回下标 } return -1;}
删除
void ContactDle(Contact* p){ char n[NAME_MAX]; printf("请输入要删除的联系人姓名:\n"); scanf("%s", n); int ret = FindName(p, n); if (ret < 0) { printf("删除对象不存在!\n"); return; } SLerase(p, ret); printf("删除成功!\n");}
显示
void ContactShow(Contact* p){ printf("%-15s%-15s%-15s%-15s%-15s\n", "姓名", "性别", "年龄", "电话", "地址"); for (int i = 0; i < p->size; i++) { printf("%-15s%-15s%-15d%-15s%-15s\n", p->a[i].name, p->a[i].gender, p->a[i].age, p->a[i].phoneNum, p->a[i].adds); }}
修改
void ContactModify(Contact* p){ char name[NAME_MAX]; printf("请输入要修改的联系人姓名:\n"); scanf("%s", name); int ret = FindName(p, name); if (ret < 0) { printf("修改对象不存在!\n"); return; } printf("请输入新的姓名:\n"); scanf("%s", p->a[ret].name); printf("请输入新的性别:\n"); scanf("%s", p->a[ret].gender); printf("请输入新的年龄:\n"); scanf("%d", &p->a[ret].age); printf("请输入新的电话:\n"); scanf("%s", p->a[ret].phoneNum); printf("请输入新的地址:\n"); scanf("%s", p->a[ret].adds); printf("修改成功!\n\n");}
查找
void ContactFind(Contact* p){ char name[NAME_MAX]; printf("请输入要查找的联系人姓名:\n"); scanf("%s", name); int ret = FindName(p, name); if (ret < 0) { printf("联系人不存在!\n"); return; } printf("%-15s%-15s%-15s%-15s%-15s\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%-15s%-15s%-15d%-15s%-15s\n", p->a[ret].name, p->a[ret].gender, p->a[ret].age, p->a[ret].phoneNum, p->a[ret].adds); printf("查询成功!\n\n");}
测试文件test.c
#include "SeqList.h"int main(){ Contact con; ContactInit(&con); while (1) { menu(); int i = 0; printf("请选择你的操作:"); scanf("%d", &i); switch (i) { case 1: ContactAdd(&con); break; case 2: ContactDle(&con); break; case 3: ContactFind(&con); break; case 4: ContactShow(&con); break; case 5: ContactModify(&con); break; case 0: printf("程序已退出!\n"); break; } } return 0;}