前言
数据结构(初阶)第一节:数据结构概论-CSDN博客
数据结构(初阶)第二节:顺序表-CSDN博客
本文将以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。
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);
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;
}