通讯录的实现(基于顺序表)

本文详细介绍了如何使用顺序表数据结构实现通讯录功能,包括顺序表的定义、结构以及通讯录类的初始化、销毁、添加、删除、修改和查找等操作的代码实现。
摘要由CSDN通过智能技术生成

通讯录(基于顺序表)

分析

我们知道通讯录存放着许多人的通讯信息

其实我们可以将通讯录理解成一个顺序表,每一个空间都存放这着一个人的通讯信息

如图所示:

image-20240417205313056

顺序表本身也是个结构体

而我们采用结构体去存放联系人信息

也就是顺序表的每个单元存放着一个结构体 里面存放着联系人信息

image-20240417205557254

这里我们可以知道

通讯录的实现是基于顺序表的

代码:

我们实现这个通讯录项目需要五个文件

分别是两个头文件 和 三个源文件

image-20240417222659973

因为该通讯录的底层就是顺序表

因此我们需要顺序表的代码:

SeqList.h 头文件

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1
# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
# include<string.h>
# include"Contact.h" //引入通讯录头文件

// 动态顺序表的定义
//typedef int SLDataType;

typedef peoInfo SLDataType;// 将我们的联系人数据 结构 作为 我们顺序表的存储元素
typedef struct SeqList
{
	SLDataType* arr;
	int size; // 记录有效数据个数
	int capacity; // 记录空间
}SL;


// 动态顺序表的功能函数声明

// 顺序表的初始化
void SLInit(SL* ps);
// 顺序表的销毁
void SLDestory(SL* ps);
// 顺序表的打印
void SLPrint(SL s);

//头部插入删除 / 尾部插入删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);

void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);

// 顺序表指定位置的插入
void SLInsert(SL* ps, int pos, SLDataType x);

// 顺序表的指定位置的删除
void SLErase(SL* ps, int pos);

// 顺序表的查找
int SLFind(SL* ps); 

注意: 这里引入了Contact.h头文件,那么在Contact.h头文件中不能再引入 SeqList.h头文件,不然会造成交叉引用 。程序会报错。

Contact.h头文件:

#pragma once

// 联系人数据
// 姓名 性别 年龄 电话 地址
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100
typedef struct personInfo 
{
	char name[NAME_MAX]; 
	char gender[GENDER_MAX]; // 性别
	int age;
	char tel[TEL_MAX]; // 电话
	char addr[ADDR_MAX]; // 地址
}peoInfo;

typedef struct SeqList Contact; // 将顺序表改个名字 改成通讯录相关的名字
//这里的 struct SeqList 是一个 外置声明

// 定义完动态顺序表 作为我们的通讯录  
// 那我们要对通讯录有着实际操作的能力 其实对通讯录操作就是对顺序表的操作

// 通讯录的初始化
void ContactInit(Contact* con);

// 通讯录的销毁
void ContactDestroy(Contact* con);

// 通讯录的添加数据
void ContactAdd(Contact* con);

// 通讯录的删除数据
void ContactDel(Contact* con);

// 通讯录的修改
void ContactModify(Contact* con);

// 通讯录的查找
void ContactFind(Contact* con);

// 通讯录的展示
void ContactShow(Contact* con);

// 通讯录的存储文件的读取
void LoadContact(Contact* con);

// 将通讯录写入文件
void SaveContact(Contact* con);

SeqList.c源文件

这里要注意了 顺序表的功能有一些在通讯录是用不上的

因为通讯录存储的数据形式是结构体类型

用来存储 联系人数据的

比如SLFind 等等就用不到需要重新编写

# include"SeqList.h"
// 这里存放着所有顺序表功能所需函数的实现

// 顺序表的初始化
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

// 顺序表的销毁
void SLDestory(SL* ps)
{
	// 判断顺序表内是否还有内容
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

 顺序表的打印
//void SLPrint(SL s)
//{
//	for (int i = 0; i < s.size; i++)
//	{
//		printf("%d ", s.arr[i]); // 这里s.arr[i] 等价于 *(s.arr + i)
//	}
//	printf("\n");
//}

// 判断空间是否足够的函数
void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		// 扩容
		// 首先判断顺序表的空间是否为0 不是 就扩容2倍 是就给4个初始空间
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));// 注意realloc函数的增容的单位是字节

		// 判断是否扩容成功
		if (tmp == NULL)
		{
			perror("realloc");
			exit(1);
		}

		// 扩容成功更新
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

// 顺序表的尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	// 首先判断空间是否足够我们去插入
	SLCheckCapacity(ps);

	// 空间足够 进行尾插
	ps->arr[ps->size] = x;
	ps->size++; // 每次插入一个数据 该顺序表元素数量+1
}

// 顺序表的头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	// 判断空间是否足够进行插入
	SLCheckCapacity(ps);

	// 头插
	// 让每个数据往后移动一个空间
	for (int 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);

	// 尾删之前需要判断 顺序表内部是否还存在元素
	assert(ps->size);

	// 尾删
	//ps->arr[ps->size - 1] = -1; 这句话不需要 也能实现
	ps->size--;
}

// 顺序表的头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);

	// 头删
	// 让全部数据向前一个 覆盖掉第一个元素 
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

// 顺序表的指定位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);

	// 对pos下标进行判断
	assert(pos >= 0 && pos <= ps->size);

	// 对顺序表的空间进行判断
	SLCheckCapacity(ps);

	// 进行插入
	// 让pos位置后的全部数据往后移动一个空间
	for (int 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);

	// 对pos进行判断
	assert(pos >= 0 && pos < ps->size);

	// 判断顺序表是否为空
	assert(ps->size > 0);

	// 进行删除
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}

	// // 只剩下一个元素的特殊情况 
	//if (ps->size == 1)
	//{
	//	ps->arr[0] = 0;
	//}

	ps->size--;
}


 顺序表的查找
//int SLFind(SL* ps, SLDataType x)
//{
//	assert(ps);
//	if (ps->size == 0)
//	{
//		printf("该顺序表为空,无法查找数据\n");
//	}
//
//	// 查找
//	for (int i = 0; i < ps->size; i++)
//	{
//		if (ps->arr[i] == x)
//		{
//			return i;
//		}
//	}
//
//	return -1;
//}

Contact.c源文件

# include "Contact.h"
# include "SeqList.h"


// 通讯录的初始化
void ContactInit(Contact* con)
{
	// 我们知道其实就是对顺序表的初始化  
	// 而我们已经在SeqList.c文件当中写好了顺序表的初始化函数
	SLInit(con);
}


// 通讯录的销毁
void ContactDestroy(Contact* con)
{
	SLDestory(con);
}


// 通讯录的展示
void PeoInfoPrint(peoInfo s) // 对传进来的联络人信息的展示
{
	printf("%2s %2s %2d %2s %2s\n", s.name, s.gender, s.age, s.tel, s.addr);
	// 这里可以手动调整格式
}
void ContactShow(Contact* con)
{
	printf("该通讯录的联系人信息如下:\n");
	// 先把表头打印出来
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
	// 把通讯录存储的每一个联系人的信息都给打印出来
	for (int i = 0; i < con->size; i++)
	{
		PeoInfoPrint(con->arr[i]);// con->arr[i]是peoInfo结构体
	}
}

// 通讯录的添加数据
void ContactAdd(Contact* con)
{
	// 获取用户输入的内容 姓名+性别+年龄+电话+地址
	// 
	// 我们之前在"Contact.h"头文件 中定义了 存储联系人数据的 结构体
	// 将用户输入的数据 全部放到 penInfo结构体中
	// penInfo结构体 其实就是我们通讯录中存储的数据类型
	peoInfo info;
	//SLDataType info; 等价于上面的peoInfo info;

	printf("请输入要添加的联系人姓名:\n");
	scanf("%s", info.name);

	printf("请输入要添加的联系人性别:\n");
	scanf("%s", info.gender);

	printf("请输入要添加的联系人年龄:\n");
	scanf("%d", &(info.age)); // 加了取地址 是因为age是整型不是数组

	printf("请输入要添加的联系人电话:\n");
	scanf("%s", info.tel);	

	printf("请输入要添加的联系人地址:\n");
	scanf("%s", info.addr);

	// 往通讯录中添加联系人数据
	SLPushBack(con, info); // 我们选择尾插来添加数据 这里可以自由选择
	printf("添加成功\n");
}

// 通过联系人的某一个特征去删除联系人的特征 
// 这里用的是名字  也可以用地址 和 电话 , 性别和年龄特征相同的太多了 不好去删除 
int FindByName(Contact* con, char name[]) 
{
	// 首先我需要遍历通讯录
	for (int i = 0; i < con->size; i++)
	{
		if (strcmp(con->arr[i].name, name) == 0)// con->arr[i]代表penInfo结构体
		{
			// 走到这里说明找到了 
			return i;
		}
	}
	// 走到这里说明没有找到
	return -1;
}

// 通讯录的删除数据
void ContactDel(Contact* con)
{
	// 输入要删除的数据
	char name[NAME_MAX];
	printf("请输入要删除的联系人的姓名:\n");
	scanf("%s", name);
	// 首先我需要去找到这个数据 我才能删除
	// 查找
	int ret = FindByName(con, name);
	if (ret >= 0) // 说明找到了需要删除的数据
	{
		SLErase(con, ret); // 将该联系人删除  这里删除是将一整个联系人的所有数据删除
		printf("你所要删除的联系人:%s,已经成功删除\n", name);
	}
	else
	{
		printf("你所要删除的联系人,不存在于通讯录中!!!\n");
		return;
	}
}

// 通讯录的修改
void ContactModify(Contact* con)
{
	// 判断你所修改的联系人是否存储在通讯录
	char name[NAME_MAX];
	printf("请输入你需要修改的用户姓名:\n");
	scanf("%s", name);

	// 通过我们自己定义的FindByName来判断是否存在
	int ret = FindByName(con, name);
	if (ret >= 0)// 找到了
	{
		// 展示联系人信息
		printf("该联系人的信息如下:\n");
		printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
		PeoInfoPrint(con->arr[ret]);

		// 修改联系人信息
		// 请选择你需要修改的数据
		int n = 0;
		do {
			printf("请选择你需要修改的数据\n");
			printf("*****************************************\n");
			printf("****1.姓名*******2.性别******3.年龄*******\n");
			printf("****4.电话*******5.地址******0.退出*******\n");
			printf("*****************************************\n");
			scanf("%d", &n);
			switch (n)
			{
			case 1:
				printf("请输入新的姓名\n");
				scanf("%s", con->arr[ret].name);
				break;
			case 2:
				printf("请输入新的性别\n");
				scanf("%s", con->arr[ret].gender);
				break;
			case 3:
				printf("请输入新的年龄\n");
				scanf("%d", &(con->arr[ret].age));
				break;
			case 4:
				printf("请输入新的电话\n");
				scanf("%s", con->arr[ret].tel);
				break;
			case 5:
				printf("请输入新的地址\n");
				scanf("%s", con->arr[ret].addr);
				break;
			case 0:
				printf("已退出修改\n");
				break;
			default:
				printf("输入不合法,请重新输入\n");
				break;
			}
		} while (n);
		printf("修改成功\n");
	}
	else // 该联系人不存在 
	{
		printf("你所要修改的联系人:%s 不在通讯录中!!!\n", name);
		return;
	}


}

// 通讯录查找
void ContactFind(Contact* con)
{
	char name[NAME_MAX];
	printf("请输入要查找的联系人姓名:\n");
	scanf("%s", name);
	
	// 判断通讯里是否有我们要查找的这个联系人
	int ret = FindByName(con, name);
	if (ret >= 0) // 找到了
	{
		printf("你所查找的联系人%s信息如下:\n", name);
		// 找到了我们就把该联系人的信息打印出来
		PeoInfoPrint(con->arr[ret]);
	}
	else
	{
		printf("你所查找的联系人不存在!!!\n");
		return;
	}
}


// 通讯录的文件读取
void LoadContact(Contact* con)
{
	// 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	// 判断文件是读取成功
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	// 将联络人数据从文件中读取出来 放到通讯录中
	int i = 0;
	peoInfo info;
	while (fread(&info, sizeof(peoInfo), 1, pf))
	{
		SLPushBack(con, info); // 将联络人数据存入通讯录中
	}
	printf("历史数据导入成功\n");

	// 关闭文件
	fclose(pf);
	pf = NULL;
}

// 通讯录中的数据存入文件当中
void SaveContact(Contact* con)
{
	// 打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	// 将通讯录中的联络人数据 写入文件中
	for (int i = 0; i < con->size; i++)
	{
		fwrite(con->arr + i, sizeof(SLDataType), 1, pf);
	}
	printf("已经成功保存联络人数据\n");

	// 关闭文件
	fclose(pf);
	pf = NULL;
}

test.c源文件:

# include"SeqList.h"
// 对通讯录的函数功能测试
/*
// 对通讯录的函数功能测试
void Test01()
{
	Contact con; // 这里创建的是顺序表的变量 
	//  等价于 SL s1
	// 通讯录初始化
	ContactInit(&con);

	// 通讯录的添加
	ContactAdd(&con);
	ContactAdd(&con);
	// 通讯录的展示
	ContactShow(&con);

	// 通讯录的删除
	ContactDel(&con);
	ContactShow(&con);

	// 通讯录的修改 
	ContactModify(&con);
	ContactShow(&con);

	// 通讯录的查找
	ContactFind(&con);

	// 通讯录的销毁
	ContactDestroy(&con);
} 


int main()
{
	Test01();

	return 0;
}
*/

void menu()
{
	printf("******************通讯录**********************\n");
	printf("********1.增加联系人   2.删除联系人************\n");
	printf("********3.修改联系人   4.查找联系人************\n");
	printf("********5.展示联系人   0.退出******************\n");
}

int main()
{
	Contact con;
	ContactInit(&con);
	while (1)
	{
		int m = 0;
		printf("是否要导入历史数据?\n");
		printf("1.yes,2.no\n");
		scanf("%d", &m);
		if (m == 1)
		{
			LoadContact(&con);// 从文件中读取联络人数据
			break;
		}
		else if (m == 2)
		{
			printf("已退出......\n");
			break;
		}
		else
			printf("输入不合法,请重新输入\n");
	}

	int n = 0;
	do {
		menu();
		printf("请选择您的操作\n");
		scanf("%d", &n);
		switch (n)
		{
		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 (n);

	while (1)
	{
		int m = 0;
		printf("是否要保存联系人数据?\n");
		printf("1.yes,2.no\n");
		scanf("%d", &m);
		if (m == 1)
		{
			SaveContact(&con); // 将联络人信息存储到文件中
			break;
		}
		else if (m == 2)
		{
			printf("已退出......\n");
			break;
		}
		else
			printf("输入不合法,请重新输入\n");
	}

	ContactDestroy(&con); // 将通讯录销毁
	return 0;
}

总结:

通讯录是基于顺序表实现的

很多方法都是可以直接调用顺序表的功能去帮助实现的

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值