数据结构(初阶):顺序表实战通讯录

前言 

数据结构(初阶)第一节:数据结构概论-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 20

typedef 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 20

typedef 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;
}

版权声明:本文为博主作者:想拿大厂offer原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/zwznzje/article/details/137512646

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2024年4月16日
下一篇 2024年4月16日

相关推荐