顺序表的使用方法
在学习顺序表之前,我们要先了解什么是数据结构:
数据结构是由“数据”和“结构”两词组合而来的。
什么是数据?
常见的数值1、2、3、4…、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页里肉眼可以看到的信息(文字、图片、视频等等),这些都是数据。
什么是结构?
当我们想要使用大量使用同一类型的数据时,通过手动定义大量的独立的变量对于程序来说,可读性非常差,我们可以借助数组这样的数据结构将大量的数据组织在一起,结构也可以理解为组织数据的方式。
概念:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的解构。
总结:
- 能够存储数据(如顺序表、链表等结构)
- 存储的数据能够方便查找
一、顺序表的概述(什么是顺序表)
顺序表的底层结构就是数组,对数组的封装,实现了常用的增删改查等接口,换句话说就是对一个数组可以实现增删查改等操作。顺序表可以分为两类:静态顺序表和动态顺序表。
静态顺序表:
概念:使用定长数组存储元素
静态顺序表的缺陷:空间大小固定,给少了空间不够用,给多了造成空间浪费。
动态顺序表:动态顺序表则与静态顺序表相反,动态顺序表是按需申请空间存储数据,给一个数组存储数据,当给的数组空间不够时即可向堆申请足够大的空间去存储数据,一般在空间不够时申请原空间的2~3倍的空间大小,按需申请即可。
typedef int SLDataType;
//动态顺序表---按需申请空间
typedef struct SeqList
{
SLDataType* arr;
int size;//记录有效数据个数
int capacity;//记录当前空间大小
}SL;
二、顺序表的初始化
顺序表的初始化部分并不难,只是简单地给结构体里的每一个成员都依次进行初始化,代码的实现过程为:先定义一个结构体,然后将结构体的地址传到“顺序表的初始化”对应的函数里再进行初始化操作。
相对应的代码如下所示:
//顺序表的初始化
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//定义结构体并传参
void SLTest01()
{
SL sl;
SLInit(&sl);
}
像这样,顺序表的初始化通过这么简简单单的几行代码就完成了。
三、顺序表的销毁
由于我们使用的是动态顺序表,而动态顺序表的使用是需要不断地进行动态空间的申请,可能会用到malloc、realloc来申请空间,既然有申请,那么就有释放,如果有空间,那么我们要手动地去释放掉。
相对应的代码如下所示:
void SLDestroy(SL* ps)
{
if (ps->arr != NULL)//如果ps->arr不为NULL,那么就需要释放
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLTest01()
{
SL sl;
SLDestroy(&sl);
}
四、顺序表的插入
顺序表的插入顾名思义就是将后来需要存储的数据插入到顺序表中,那么如果需要插入数据就需要判断顺序表的空间够不够,如果不够再向堆去申请足够的空间再进行数据的存储。
顺序表的扩容首先需要判断顺序表的空间大小是否足以容纳将要存储的数据,如果空间不足,再向堆申请一定的空间;空间申请完成之后就可以将数据插入扩容后的顺序表中了。
void SLCheckCapacity(SL * ps)//顺序表的扩容
{
if (ps->size == ps->capacity)//判断空间大小是否等于有效数据个数
{
int NewCapacity = ps->capacity = 0 ? 4 : ps->capacity * 2;
SLDataType* temp = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType));
if (temp == NULL)//申请空间失败
{
perror("realloc fail!");
return;//提前返回
}
//申请空间成功
ps->arr = temp;
ps->capacity = NewCapacity;
}
}
4.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]=ps->arr[i]
}
//将新的数据插入头部
ps->arr[0] = x;
ps->size++;
}
4.2顺序表的尾插
顺序表的尾插就是将新的数据插入到顺序表的尾部:
void SLPushBack(SL* ps, SLDataType x)//顺序表的尾插
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
//后面两行代码也可以写成ps->arr[ps->size++]
}
4.3在顺序表的指定位置插入数据
在顺序表的任意位置插入数据就是想往哪插就往哪插,当然也包括头插和尾插,也就是说只要懂得这个代码怎么写就不用写头插和尾插代码了。
void SLInsert(SL* ps, int pos, SLDataType x)//在指定位置插入数据
{
assert(ps);
if (pos >= 0 && pos <= ps->size)//检查插入的位置是否合法
{
//检查空间是否足够
SLCheckCapacity(ps);
//移动数据
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];//ps->arr[i]=ps->arr[i-1]
}
ps->arr[pos] = x;//插入新数据
ps->size++;
}
}
五、顺序表的删除
顺序表的删除就不做过多的解释了,大概意思和顺序表的插入几乎就是一个意思,接下来的各种操作也就不过多的解释了,直接展示代码。
5.1顺序表的头删
void SLPopFront(SL* ps)//顺序表的头删
{
assert(ps);
assert(ps->size);//断言防止顺序表没有数据可以删除
for (int i = 0; i < ps->size-1; i++)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->size--;
}
5.2顺序表的尾删
顺序表的尾删相对于头删就更简单了,不需要将其他数据移动,只要将尾部数据删除即可完成尾删代码的编写:
void SLPopBack(SL* ps)//顺序表的尾删
{
assert(ps);
assert(ps->size);
ps->size--;//size--即可将尾部的数据覆盖掉,就完成了尾删操作
}
5.3删除顺序表指定位置的数据
void SLErase(SL* ps, int pos)//删除指定位置的数据
{
assert(ps);
//判断删除的数据是否合法
if (pos >= 0 && pos < ps->size)
{
// 删除数据
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];//ps->[size-2]=ps->arr[size-1]
}
ps->size--;
}
六、顺序表的查找
int SLFind(SL* ps, SLDataType x)//顺序表的查找
{
assert(ps);//断言防止传入空指针
//遍历顺序表
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;//返回下标
}
}
// 没有找到
return -1;//返回一个不存在的下标
}
七、运用顺序表实现通讯录项目
学习完了顺序表的内容,那么通讯录项目也就轻而易举了,通讯录项目就是在顺序表代码的基础上再增添、修饰下完成的。其大部分代码还是依靠顺序表的实现来完成的。
前面说了通讯录是基于顺序表实现的,而顺序表的底层结构就是数组,是通过结构体的定义来实现的,那么我们同样可以通过再定义一个结构体来创建一个通讯录,其中顺序表的每一个空间存储一个人的数据,这样就可以实现通讯录项目了。其形式如下:
定义的通讯录结构体如下:
typedef struct SeqList Contact;
typedef struct Contact
{
char name[name_MAX];//姓名
char sex[sex_MAX];//性别
int age;//年龄
char add[add_MAX];//地址
char tel[tel_MAX];//电话
}peoInfo;
最后再利用所学到的顺序表知识,调用顺序表的实现代码,对通讯录进行增删查改操作,实现通讯录项目。
#include"Contact.h"
#include"Seqlist.h"
//通讯录的初始化
void ConInit(Contact* con)
{
InitSL(con);
}
//通讯录的销毁
void DestroyCon(Contact* con)
{
DestroySL(con);
}
//通讯录的增加
void AddCon(Contact* con)
{
//获取要输入的联系人姓名+性别+年龄+地址+电话
peoInfo info;//已经有了结构体存储数据 不需要再创建char类型来存储数据
//即char name[ ];
printf("请输入要添加的联系人的姓名:\n");
scanf("%s", info.name);
printf("请输入要添加的联系人的性别:\n");
scanf("%s", info.sex);
printf("请输入要添加的联系人的年龄:\n");
scanf("%d", &info.age);
printf("请输入要添加的联系人的地址:\n");
scanf("%s", info.add);
printf("请输入要添加的联系人的电话:\n");
scanf("%s", info.tel);
AddBackSL(con, info);//顺序表已有方法的复用
}
//检查通讯录内是否有某个用户
int FindName(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(name, con->arr[i].name) == 0)
return i;//找到了
}
return -1;//没有找到
}
//通讯录的删除
void ConErase(Contact* con)
{
//首先确定要删除的数据是否存在,才能执行删除操作
char name[name_MAX];
printf("请输入要删除的联系人的姓名:\n");
scanf("%s", name);
int find = FindName(con, name);
if (find < 0)
{
printf("您要删除的联系人数据不存在!");
return;
}
//要删除的联系人数据存在---数据的下标为find
SLErase(con, find);
}
//通讯录的修改
void ModifyCon(Contact* con)
{
char name[name_MAX];
printf("请输入要修改信息的联系人的姓名:\n");
scanf("%s", name);
int find = FindName(con, name);
if (find < 0)
{
printf("您要修改数据的联系人信息不存在!");
return;
}
//要修改的联系人数据存在---数据的下标为find
printf("请输入新的联系人的姓名:\n");
scanf("%s", con->arr[find],name);
printf("请输入新的联系人的性别:\n");
scanf("%s", con->arr[find].sex);
printf("请输入新的联系人的年龄:\n");
scanf("%d", &con->arr[find].age);
printf("请输入新的联系人的地址:\n");
scanf("%s", con->arr[find].add);
printf("请输入新的联系人的电话:\n");
scanf("%s", con->arr[find].tel);
printf("修改成功!\n");
}
//通讯录的查找
void FindCon(Contact* con)
{
char name[name_MAX];
printf("请输入要查找的联系人的姓名:\n");
scanf("%s", name);
int find = FindName(con, name);
if (find < 0)
{
printf("您要查找的联系人信息不存在!");
return;
}
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "地址", "电话");
printf("%s %4s %4d %4s %4s\n", con->arr[find].name,
con->arr[find].sex,
con->arr[find].age,
con->arr[find].add,
con->arr[find].tel);
}
//通讯录的展示
void ShowCon(Contact* con)
{
//表头
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "地址", "电话");
for (int i = 0; i < con->size; i++)
{
printf("%s %4s %4d %4s %4s\n",con->arr[i].name,
con->arr[i].sex,
con->arr[i].age,
con->arr[i].add,
con->arr[i].tel);
}
}
通过这样简单的增加、修饰,通讯录项目就实现了。今天的分享就到这了,要完成通讯录的编写,还要靠你们自己去实践,感谢大家的阅读,我们下期再见!