系列文章目录
【数据结构】顺序表
文章目录
- 系列文章目录
- 前言
- 一、通讯录的功能要求
- 二、通讯录的代码实现
- 1. 新建文件
- 2. 创建通讯录的结构体
- 3. 对顺序表文件进行修改
- 4. 通讯录具体功能实现
- 4.1. 通讯录的初始化和销毁
- 4.2. 增加联系人信息(尾插)
- 4.3. 查找指定联系人(通过姓名查找)
- 4.4. 删除指定联系人
- 4.5. 修改指定联系人
- 4.6. 显示联系人信息
- 三、完整代码
- 四、效果展示
- 总结
前言
回顾上文,我们初步了解了顺序表,顺序表的本质就是数组,但相比于单纯的数组,顺序表多出了一些功能——增删查改。那么顺序表究竟有什么用呢?接下来为大家使用顺序表实现一个简易的通讯录。(使用动态顺序表实现)
正文开始
一、通讯录的功能要求
- 至少能够存储100个人的通讯信息
- 能够保存用户信息:名字、性别、年龄、电话、地址等
- 增加联系人信息
- 删除指定联系人
- 查找指定联系人
- 修改指定联系人
- 显示联系人信息
二、通讯录的代码实现
1. 新建文件
顺序表作为一种数据结构,只是一个工具,当我们要使用它去实现具体的东西时还需要对其进行包装。所以,我们这里在原来顺序表的基础上新建了两个文件——Contact.h和Contact.c。
Contact.h中存放通讯录所需的函数的声明和结构体的声明。
Contact.c中存放通讯录中函数的定义。
2. 创建通讯录的结构体
接下来我们就需要在Contact.h中创建一个通讯录的结构体,要包含的信息有:名字、性别、年龄、电话、地址。所以我们的顺序表中的元素的类型要是结构体,该结构体有5个成员。
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 20
typedef struct personinform
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}perinfo;
由于我们要实现通讯录,我们的顺序表的结构体的结构体名要更改为Contact,且要在该头文件中就要实现,因为后面有对应函数的声明,所以我们要在Contact.h中使用前置声明。
typedef struct Sepline Contact; // 只有前置声明才能更改顺序表的结构体名。
3. 对顺序表文件进行修改
我们在Contact.h中把套在顺序表上的外壳写完,我们还要对原来的顺序表进行小幅度修改,使其与我们要实现的功能匹配。
首先就要包含Contact.h这个头文件,然后将typedef后的int改为perinfo。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Contact.h"
typedef perinfo SLtype;
//动态顺序表
typedef struct Sepline
{
SLtype* arr;
int size;//有效数字大小
int capacity;//动态内存大小
}SL;
最后,我们要将顺序表的函数实现进行修改,原本是实现int的,现在要改为实现结构体形式。
void SLshow(SL* sl)
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < sl->size; i++)
printf("%3s %3s %3d %3s %3s\n",
sl->arr[i].name,
sl->arr[i].gender,
sl->arr[i].age,
sl->arr[i].tel,
sl->arr[i].addr);
}
顺序表的修改和查找,我选择删除重新实现,要修改与重写没什么区别。
4. 通讯录具体功能实现
4.1. 通讯录的初始化和销毁
//初始化
void Con_init(Contact* con);
//销毁
void Con_des(Contact* con);
void Con_init(Contact* con)
{
SLinit(con);
}
void Con_des(Contact* con)
{
SLdestory(con);
}
这就是使用了顺序表后的情况,在具体实现函数时只需要套用之前的函数就可以了。
4.2. 增加联系人信息(尾插)
//插入(后插)
void Con_insert(Contact* con);
void Con_insert(Contact* con)
{
perinfo x = { 0 };
printf("请输入联系人姓名:");
scanf("%s", x.name);
printf("请输入联系人性别:");
scanf("%s", x.gender);
printf("请输入联系人年龄:");
scanf("%d", &x.age);
printf("请输入联系人电话:");
scanf("%s", x.tel);
printf("请输入联系人地址:");
scanf("%s", x.addr);
SL_in_back(con, x);
}
4.3. 查找指定联系人(通过姓名查找)
int Con_find_name(Contact* con, char name[]);
int Con_find_name(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(name, con->arr[i].name))
return i;
}
return -1;
}
4.4. 删除指定联系人
//指定删除
void Con_del(Contact* con);
void Con_del(Contact* con)
{
char name[20];
printf("请输入需要删除的联系人:");
scanf("%s", name);
int find = Con_find_name(con, name);
if (find != -1)
{
SLindel(con, find);
printf("删除成功\n");
}
else
{
printf("需要删除的联系人不存在\n");
}
}
4.5. 修改指定联系人
//修改
void Con_fix_name(Contact* con);
void Con_fix_name(Contact* con)
{
char name[20];
printf("请输入需要修改的联系人姓名:");
scanf("%s", name);
int find = Con_find_name(con, name);
perinfo x = { 0 };
printf("请输入修改后的姓名:");
scanf("%s", x.name);
printf("请输入修改后的性别:");
scanf("%s", x.gender);
printf("请输入修改后的年龄:");
scanf("%d", &x.age);
printf("请输入修改后的电话:");
scanf("%s", x.tel);
printf("请输入修改后的地址:");
scanf("%s", x.addr);
con->arr[find] = x;
}
4.6. 显示联系人信息
//展示
void Con_show(Contact* con);
void Con_show(Contact* con)
{
SLshow(con);
}
这些通讯录的函数的实现基本都通过调用了顺序表的函数,大大减少了我们需要写的代码量,而且还不用考虑数据是如何增加,删除等。当然,前提是顺序表的函数没有写错。
三、完整代码
Contact.h
#pragma once
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 20
typedef struct personinform
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}perinfo;
typedef struct Sepline Contact; // 只有前置声明才能更改顺序表的结构体名。
//初始化
void Con_init(Contact* con);
//销毁
void Con_des(Contact* con);
//插入(后插)
void Con_insert(Contact* con);
//指定删除
void Con_del(Contact* con);
//展示
void Con_show(Contact* con);
//查找
int Con_find_name(Contact* con, char name[]);
//修改
void Con_fix_name(Contact* con);
Sepline.h
#pragma once
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Contact.h"
typedef perinfo SLtype;
//动态顺序表
typedef struct Sepline
{
SLtype* arr;
int size;//有效数字大小
int capacity;//动态内存大小
}SL;
//顺序表初始化
void SLinit(SL* sl);
//顺序表销毁
void SLdestory(SL* sl);
//后插
void SL_in_back(SL* sl, SLtype x);
//前插
void SL_in_front(SL* sl, SLtype x);
//后删
void SL_out_back(SL* sl);
//前删
void SL_out_front(SL* sl);
//展示
void SLshow(SL* sl);
//查找
int SLfind(SL* sl, SLtype n);
//修改
void SLfix(SL* sl, int n);
//指定位置增加
void SLinsert(SL* sl, int n, SLtype x);
//指定位置删除
void SLindel(SL* sl, int n);
//扩展空间
void SLapply(SL* sl);
Contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Sepline.h"
#include "Contact.h"
void Con_init(Contact* con)
{
SLinit(con);
}
void Con_des(Contact* con)
{
SLdestory(con);
}
void Con_insert(Contact* con)
{
perinfo x = { 0 };
printf("请输入联系人姓名:");
scanf("%s", x.name);
printf("请输入联系人性别:");
scanf("%s", x.gender);
printf("请输入联系人年龄:");
scanf("%d", &x.age);
printf("请输入联系人电话:");
scanf("%s", x.tel);
printf("请输入联系人地址:");
scanf("%s", x.addr);
SL_in_back(con, x);
}
int Con_find_name(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(name, con->arr[i].name))
return i;
}
return -1;
}
void Con_del(Contact* con)
{
char name[20];
printf("请输入需要删除的联系人:");
scanf("%s", name);
int find = Con_find_name(con, name);
if (find != -1)
{
SLindel(con, find);
printf("删除成功\n");
}
else
{
printf("需要删除的联系人不存在\n");
}
}
void Con_show(Contact* con)
{
SLshow(con);
}
void Con_fix_name(Contact* con)
{
char name[20];
printf("请输入需要修改的联系人姓名:");
scanf("%s", name);
int find = Con_find_name(con, name);
perinfo x = { 0 };
printf("请输入修改后的姓名:");
scanf("%s", x.name);
printf("请输入修改后的性别:");
scanf("%s", x.gender);
printf("请输入修改后的年龄:");
scanf("%d", &x.age);
printf("请输入修改后的电话:");
scanf("%s", x.tel);
printf("请输入修改后的地址:");
scanf("%s", x.addr);
con->arr[find] = x;
}
Sepline.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Sepline.h"
void SLinit(SL* sl)
{
sl->arr = NULL;
sl->size = sl->capacity = 0;
}
void SLdestory(SL* sl)
{
free(sl->arr);
sl->arr = NULL;
sl->size = sl->capacity = 0;
}
void SLapply(SL* sl)
{
if (sl->size == sl->capacity)
{
sl->capacity = sl->capacity == 0 ? 4 : 2 * sl->capacity;
SLtype* tmp = (SLtype*)realloc(sl->arr, sl->capacity * sizeof(SLtype));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
sl->arr = tmp;
tmp = NULL;
}
}
void SL_in_back(SL* sl, SLtype x)
{
assert(sl);
SLapply(sl);
sl->arr[sl->size++] = x;
}
void SL_in_front(SL* sl, SLtype x)
{
assert(sl);
SLapply(sl);
for (int i = sl->size; i > 0; i--)
{
sl->arr[i] = sl->arr[i - 1];
}
sl->arr[0] = x;
sl->size++;
}
void SL_out_back(SL* sl)
{
assert(sl);
assert(sl->size);
sl->size--;
}
void SLshow(SL* sl)
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < sl->size; i++)
printf("%3s %3s %3d %3s %3s\n",
sl->arr[i].name,
sl->arr[i].gender,
sl->arr[i].age,
sl->arr[i].tel,
sl->arr[i].addr);
}
void SL_out_front(SL* sl)
{
assert(sl);
assert(sl->size);
for (int i = 0; i < sl->size - 1; i++)
sl->arr[i] = sl->arr[i + 1];
sl->size--;
}
//int SLfind(SL* sl, SLtype n)
//{
// assert(sl);
// //assert(n >= 0 && n < sl->size);
// for (int i = 0; i < sl->size; i++)
// {
// if (n == sl->arr[i])
// return i;
// }
// return -1;
//}
//void SLfix(SL* sl, int n)
//{
// assert(sl);
// assert(n >= 0 && n < sl->size);
// int num = 0;
// printf("请输入修改后的数:");
// scanf("%d", &num);
// sl->arr[n] = num;
//}
void SLinsert(SL* sl, int n, SLtype x)
{
assert(sl);
assert(n >= 0 && n <= sl->size);
SLapply(sl);
for (int i = sl->size; i > n; i--)
{
sl->arr[i] = sl->arr[i - 1];
}
sl->arr[n] = x;
sl->size++;
}
void SLindel(SL* sl, int n)
{
assert(sl);
assert(n >= 0 && n < sl->size);
for (int i = n; i < sl->size - 1; i++)
{
sl->arr[i] = sl->arr[i + 1];
}
sl->size--;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Sepline.h"
#include "Contact.h"
void menu()
{
printf("\n");
printf("***********通讯录**********\n");
printf("**** 1. 增加 2. 删除 ****\n");
printf("**** 3. 查找 4. 展示 ****\n");
printf("**** 5. 修改 0. 退出 ****\n");
printf("***************************\n");
}
void test()
{
Contact con;
char name[20];
int choose = 0;
Con_init(&con);
do
{
menu();
printf("请输入你要进行的操作(输入前面数字):");
scanf("%d", &choose);
switch (choose)
{
case 1: Con_insert(&con);
break;
case 2: Con_del(&con);
break;
case 3:
printf("请输入你要查找的联系人姓名:");
scanf("%s", name);
int find = Con_find_name(&con, name);
if (find < 0)
printf("没找到\n");
else
printf("找到了,在第%d个\n", find);
break;
case 4: Con_show(&con);
break;
case 5: Con_fix_name(&con);
break;
case 0: printf("退出应用\n");
break;
default: printf("请输入0~6之间的数\n");
break;
}
} while (choose);
Con_des(&con);
}
int main()
{
test();
return 0;
}
四、效果展示
增加联系人
修改联系人
删除联系人
展示联系人
查找联系人
总结
通讯录的实现实际十分简单,我们只要能熟练掌握顺序表的功能的实现,再在顺序表上套一个外壳就变成了通讯录。我们这个通讯录还有点小问题——无法一直保存数据。如果想要一直保存可以使用文件来操作,将每次修改的数据保存到一个文件里,每次进入程序先从该文件中读取数据,结束时将数据传回文件中。大家可以试着实现以下。
这里是我的解决方法:
test.c
void test()
{
FILE* pf = fopen("Contact.txt", "r");
Contact con;
char name[20];
int choose = 0;
Con_init(&con);
Con_insert_file(&con, pf);
do
{
menu();
printf("请输入你要进行的操作(输入前面数字):");
scanf("%d", &choose);
switch (choose)
{
case 1: Con_insert(&con);
break;
case 2: Con_del(&con);
break;
case 3:
printf("请输入你要查找的联系人姓名:");
scanf("%s", name);
int find = Con_find_name(&con, name);
if (find < 0)
printf("没找到\n");
else
printf("找到了,在第%d个\n", find);
break;
case 4: Con_show(&con);
break;
case 5: Con_fix_name(&con);
break;
case 0: printf("退出应用\n");
break;
default: printf("请输入0~6之间的数\n");
break;
}
} while(choose);
fclose(pf);
pf = NULL;
pf = fopen("Contact.txt", "w");
for (int i = 0; i < con.size; i++)
{
fprintf(pf, "%s %s %d %s %s\n",
con.arr[i].name,
con.arr[i].gender,
con.arr[i].age,
con.arr[i].tel,
con.arr[i].addr);
}
fclose(pf);
pf = NULL;
Con_des(&con);
}
Contact.c
void Con_insert_file(Contact* con, FILE* pf)
{
int ret = 0;
int i = 0;
while (ret != EOF)
{
SLapply(con);
ret = fscanf(pf, "%s %s %d %s %s",
con->arr[i].name,
con->arr[i].gender,
&con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr);
if (ret != EOF)
{
con->size++;
i++;
}
}
}
Contact.h
//文件插入
void Con_insert_file(Contact* con, FILE* pf);
感谢观看!!!