(数据结构)—— 顺序表的应用-通讯录的实现
- 一.通讯录的功能介绍
- 1.基于动态顺序表实现通讯录
- (1). 功能要求
- (2).重要思考
- 二. 通讯录的代码实现
- 1.通讯录的底层结构(顺序表)
- (1)思路展示
- (2)底层代码实现(顺序表)
- 2.通讯录上层代码实现(通讯录结构)
- (1).思路展示
- (2).上层代码实现(通讯录)
- 3.通讯录代码运行展示(数据只用于测试,无实际意义)
- (1)测试展示
- (2)升华总结
一.通讯录的功能介绍
1.基于动态顺序表实现通讯录
C语言基础要求:结构体、动态内存管理、顺序表、文件件操作
(1). 功能要求
1)至少能够存储100个人的通讯信息
2)能够保存用户信息:名字、性别、年龄、电话、地址等
3)增加联系人信息
4)删除指定联系人
5)查找制定联系人
6)修改指定联系人
7)显示联系人信息
(2).重要思考
【思考1】用静态顺序表和动态顺序表分别如何实现
【思考2】如何保证程序结束后,历史通讯录信息不会丢失
二. 通讯录的代码实现
1.通讯录的底层结构(顺序表)
(1)思路展示
由于,通讯录是基于动态顺序表实现的,所以掌握基本的顺序表的结构代码,非常重要~!
(2)底层代码实现(顺序表)
1.
SeqList.h
#pragma once
#include"Contact.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//typedef int SLDataType;
//更改数据类型为通讯录数据类型
//以下的方式任意选择即可
typedef struct ContactInfo SLDataType;
//typedef CInfo SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;//当前顺序表中的数据有效个数
int capacity;//顺序表的当前空间的大小
}SL;
//typedef struct SeqList SL;
//对顺序表进行初始化
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//头部/尾部/插入/删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//任意位置/插入/删除
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
//打印
void SLPrint(SL* ps);
bool SLIsEmpty(SL* ps);
//查找
bool SLFind(SL* ps, SLDataType x);
2.
SeqList.c
#include"SeqList.h"
//初始化顺序表
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps)
{
if (ps->a)
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
//空间不足以插入一个数据,需要扩容
if (ps->size == ps->capacity)
{
//扩容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc Fail!\n");
return 1;
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
//判断顺序表是否为空
//assert(ps->a = NULL);
//暴力方式
assert(ps);
//柔和的方式
/*if (ps->a == NULL)
return;*/
//1)空间足够,直接插入
//2)空间不够,需要扩容
SLCheckCapacity(ps);
//空间足够,直接插入
ps->a[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
//空间足够,历史数据后移一位;
for (size_t i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
//ps->a[ps->size - 1] = 0;
ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
for (size_t i = 1; i < ps->size - 1; i++)
{
//最后一次进来的是ps->a[ps->size-2]
ps->a[i] = ps->a[i + 1];//pa->a[ps->size-2]=ps->a[ps->size-1]
}
ps->size--;
}
//任意位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
//判断插入的位置是否在范围内
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
//空间足够,把pos的位置及以后的数据往后移一位
//此处i<ps->size和ps->size-1都可以,但是后面的不步骤需要对应
for (size_t i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
/*for (size_t i = ps->size - 1; i > pos; i--)
{
ps->a[i+1] = ps->a[i];
}*/
ps->a[pos] = x;
ps->size++;
}
//任意位置删除
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(!SLIsEmpty(ps));
assert(pos >= 0 && pos < ps->size);
//pos位置及以后的数据往前移动一位
for (size_t i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
void SLPrint(SL* ps)
{
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
bool SLIsEmpty(SL* ps)
{
assert(ps);
//这样是不对的,这只是判断空间是否足够
//return ps->size = ps->capacity;
return ps->size == 0;
}
//bool SLFind(SL* ps, SLDataType x)
//{
// scanf("%d", &x);
// for (size_t i = 0; i < ps->size; i++)
// {
// if (ps->a[i] == x)
// {
// return true;
// }
// }
// return false;
//}
3.
test.c
#include"SeqList.h"
#include"Contact.h"
//void SLtest()
//{
//SL sl;
//SLInit(&sl);
顺序表的具体操作
尾插
//SLPushBack(&sl, 1);
//SLPushBack(&sl, 2);
//SLPushBack(&sl, 3);
//SLPushBack(&sl, 4);
//SLPrint(&sl);
头插
//SLPushFront(&sl, 5);
//SLPushFront(&sl, 6);
//SLPushFront(&sl, 7);
//SLPushFront(&sl, 8);
//SLPrint(&sl);
//尾删
//SLPopBack(&sl);
//SLPrint(&sl);
//SLPopBack(&sl);
//SLPrint(&sl);
头删
//SLPopFront(&sl);
//SLPrint(&sl);
//SLPopFront(&sl);
//SLPrint(&sl);
//任意位置插入删除
/*SLInsert(&sl, 0, 9);
SLPrint(&sl);
SLErase(&sl, 8);
SLPrint(&sl);
bool ret = SLFind(&sl, 9);
if (ret)
printf("找到了\n");
else
printf("没找到\n");
SLDestroy(&sl);*/
//}
void Contact01()
{
contact con;
ContactInit(&con);
//往通讯录中插入数据
ContactAdd(&con);
ContactAdd(&con);
ContactShow(&con);
//从通讯录里删除指定的数据
ContactDel(&con);
ContactShow(&con);
//从通讯录里查找指定的联系人
ContactFind(&con);
//在通讯录里修改指定的联系人
ContactModify(&con);
ContactDestroy(&con);
}
void menu()
{
printf("***************通讯录****************\n");
printf("***** 1.添加联系人 2.删除联系人*****\n");
printf("***** 3.修改联系人 4.查找联系人*****\n");
printf("***** 5.查看通讯录 0.退出通讯录*****\n");
printf("*************************************\n");
}
int main()
{
//SLtest();
//Contact01();
int op = -1;
contact con;
ContactInit(&con);
do
{
menu();
printf("请选择你要进行的操作:\n");
scanf("%d", &op);
switch (op)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactModify(&con);
break;
case 4:
ContactFind(&con);
break;
case 5:
ContactShow(&con);
break;
case 0:
printf("已退出通讯录\n");
break;
default:
printf("系统里没有找到你需要进行的操作,请重新输入:\n");
break;
}
} while (op != 0);
ContactDestroy(&con);
return 0;
}
2.通讯录上层代码实现(通讯录结构)
(1).思路展示
1)由于,通讯录是基于动态顺序表实现的。
2)所以,通讯录上层代码,只需要在上上层代码的头文件代码中创建一个保存联系人的结构,然后再定义通讯录功能函数即可。
3)接着在通讯录上层代码的源文件中将这些功能函数实现即可。
4)基于前面,我们已经知道,通讯录是基于动态顺序表实现的,所以在实现通讯录功能函数时,只需要在通讯录上层代码的源文件中,加上顺序表的头文件就可以调运顺序表的函数来实现通讯录了。
(2).上层代码实现(通讯录)
1.
Contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
//创建保存联系人的结构
#define NAME_MAX 100
#define SEX_MAX 10
#define TEL_MAX 15
#define ADDR_MAX 100
struct ContactInfo
{
char name[NAME_MAX];//名字
char sex[SEX_MAX];//性别
int age;//年龄
char tel[TEL_MAX];//电话
char addr[ADDR_MAX];//家庭住址
};
typedef struct ContactInfo CInfo;
//通讯录底层是用顺序表来实现的
typedef struct SeqList contact;
//通讯录的初始化和销毁
void ContactInit(contact* pcon);
void ContactDestroy(contact* pcon);
//添加联系人
void ContactAdd(contact* pcon);
//删除联系人
void ContactDel(contact* pcon);
//修改联系人
void ContactModify(contact* pcon);
//查看通讯录
void ContactShow(contact* pcon);
//查找联系人
void ContactFind(contact* pcon);
2.
Contact.c
#include"Contact.h"
#include"SeqList.h"
//通讯录的初始化与销毁
void ContactInit(contact* pcon)
{
SLInit(pcon);
}
void ContactDestroy(contact* pcon)
{
SLDestroy(pcon);
}
//添加联系人
void ContactAdd(contact* pcon)
{
//接下来获取的都是结构体CInfo结构体里要求的数据
CInfo info;
printf("请输入联系人姓名:\n");
scanf("%s", info.name);
printf("请输入联系人性别:\n");
scanf("%s", info.sex);
printf("请输入联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入联系人电话:\n");
scanf("%s", info.tel);
printf("请输入联系人住址:\n");
scanf("%s", info.addr);
//联系人数据都获取到了,并保存到了结构体info中
//往通讯录(顺序表)中插入数据
SLPushBack(pcon, info);
}
int FindByName(contact* pcon, char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->a[i].name, name) == 0)
{
return i;
}
}
return -1;
}
//删除联系人
void ContactDel(contact* pcon)
{
//直接强制用户使用名称来查找
printf("请输入要删除的联系人姓名:\n");
char name[NAME_MAX];
scanf("%s", name);
int findidex = FindByName(pcon, name);
if (findidex < 0)
{
printf("要删除的联系人不存在!\n");
return;
}
//找到了,要删除findidex位置的数据
SLErase(pcon, findidex);
printf("删除成功\n");
}
//修改联系人选项
void MondifyMenu()
{
printf("***************修改联系人选项****************\n");
printf("***** 1.修改联系人名字 2.修改联系人性别*****\n");
printf("***** 3.修改联系人年龄 4.修改联系人电话*****\n");
printf("***** 5.修改联系人住址 0.退出删除选项*******\n");
printf("******************************************\n");
}
//修改联系人
void ContactModify(contact* pcon)
{
printf("请输入你需要修改的联系人:\n");
char name[NAME_MAX];
scanf("%s", name);
int find = FindByName(pcon, name);
if (find < 0)
{
printf("要修改的联系人不存在!\n");
return;
}
printf("找到了:\n");
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-5s %-5s %-4d %-4s %-4s\n",
pcon->a[find].name,
pcon->a[find].sex,
pcon->a[find].age,
pcon->a[find].tel,
pcon->a[find].addr);
int val = -1;
do
{
MondifyMenu();
printf("请选择你需要修改的信息:\n");
scanf("%d", &val);
switch (val)
{
case 1:
printf("请输入修改后的联系人姓名:\n");
scanf("%s", pcon->a[find].name);
break;
case 2:
printf("请输入修改后的联系人性别:\n");
scanf("%s", pcon->a[find].sex);
break;
case 3:
printf("请输入修改后的联系人年龄:\n");
scanf("%d", &pcon->a[find].age);
break;
case 4:
printf("请输入修改后的联系人电话:\n");
scanf("%s", pcon->a[find].tel);
break;
case 5:
printf("请输入修改后的联系人住址:\n");
scanf("%s", pcon->a[find].addr);
break;
case 0:
printf("修改成功\n");
printf("已退出删除联系人选项\n");
break;
default:
printf("没有找到要修改的选项 请重新选择:\n");
break;
}
} while (val != 0);
}
//查看通讯录
void ContactShow(contact* pcon)
{
//打印表头
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
for (int i = 0; i < pcon->size; i++)
{
printf("%-5s %-5s %-4d %-4s %-4s\n",
pcon->a[i].name,
pcon->a[i].sex,
pcon->a[i].age,
pcon->a[i].tel,
pcon->a[i].addr);
}
}
//查找指定联系人
void ContactFind(contact* pcon)
{
printf("请输入你要查找的联系人:\n");
char name[NAME_MAX];
scanf("%s", name);
int findidex = FindByName(pcon, name);
if (findidex < 0)
{
printf("你要查找的联系人不存在!\n");
return;
}
printf("找到了:\n");
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-5s %-5s %-4d %-4s %-4s\n",
pcon->a[findidex].name,
pcon->a[findidex].sex,
pcon->a[findidex].age,
pcon->a[findidex].tel,
pcon->a[findidex].addr);
}
3.通讯录代码运行展示(数据只用于测试,无实际意义)
(1)测试展示
(2)升华总结
这里只是展示基于动态顺序表实现通讯录的大致框架,和一些基本功能,有兴趣,能力的小伙伴,也可以下去拓展一下通讯录的其他功能,优化的更加完善,美观的通讯录。
感谢学习!