顺序表
顺序表的概念
• 顺序表是线性表的一种,即顺序表的逻辑结构是线性结构。
• 顺序表的物理结构也是线性的。
• 顺序表的底层是数组。
• 顺序表又分为静态顺序表和动态顺序表,如下:
//静态顺序表
struct Seqlist
{
int a[100];//静态数组
int size;//有效数据个数
}
//动态数据表
struct Seqlist
{
int*a;
int size;//有效数据个数
int capacity;//顺序表当前的空间大小
}
• 实际情境中,使用动态顺序表的情况远远多于使用静态数据表的情况。因为,静态数据表空间少了会导致数据泄露、空间多了会导致空间浪费。
顺序表的创建
这里我们用动态顺序表作示范,此外,为了方便顺便表存储的数据类型可以方便改变,我们用typedef来重定义类型名:
typedef int SLDataType;
typedef struct SeqList
{
SLDataType*a;
int size;//有效数据个数
int capacity;//顺序表空间大小
}SL;
顺序表初始化和销毁
• 注意实现初始化时,要传址调用。如果传值调用,那么只会对形参初始化。
void SLInit(SL*sp)
{
SL->a=NULL;
SL->size=SL->capacity=0;
}
void SLDestroy(SL*sp)
{
if(SL->a)
{
free(SL->a);
SL->a=NULL;
}
SL=NULL:
}
顺序表插入与删除
• 插入的方式有头插、尾插
• 删除的方式有头删、尾删
• 插入前需要判断空间是否足够、如果不够的话需要开辟空间。注意,频繁的扩容会降低数组的性能。为了提高性能我们通常进行1.5倍或者2倍的增容。
//检测是否空间足够
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)
{
perror("realloc");
return 1;
]
ps->a=tmp;
ps->capacity=NewCapacity;
}
}
//尾插
void SLPushBack(SL*ps,SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size++]=x;
}
//头插
void SLPushFront(SL*ps,SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
for(unsigned int i=ps->size;i>0;i--)
{
ps->a[i]=ps->a[i-1];
}
ps->a[0]=x;
}
//尾删
void SLPopBack(SL*ps)
{
assert(ps&&ps->size);//size为0时不能删除
ps->size--;
}
//头删
void SLPopFront(SL*ps)
{
assert(ps&&ps->size);
for(int i=0;i<size-1;i++)
{
ps->a[i]=ps->a[i+1];
}
ps->size--;
}
//在指定位置之前插入数据
void SLInset(SL*ps,int pos,SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);//可以在size处插入,相当于尾插
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;
ps->size++;
}
//删除指定位置数据
void SLErase(SL* ps, int pos)
{
assert(ps && ps->size);
assert(pos >= 0 && pos < ps->size);//不能在size处删除,因为不是有效数据
for (int i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
顺序表的查找
当我们存储数据后,肯定想知道顺序表里面有没有想要的数据,这时可以定义一个查找函数。
bool SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (x == ps->a[i])
return true;
}
return false;
}
那么以上就是顺序表的主要功能,接下来我们将基于顺序表实现通讯录。
通讯录
首先我们知道一个通讯录中通常存储电话号码、姓名、性别、住址、年龄等数据。我们要创建相应的结构体来存储数据。
创建
#define NAME_MAX 100
#define SEX_MAX 10
#define TEL_MAX 15
#define ADDR_MAX 100
typedef struct ContactInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}CInfo;
初始化和销毁
·由于通讯录是基于顺序表实现的,所以我们能调用前面实现的顺序表函数来实现各种通讯录的功能。
void ContactInit(contact*pcon)
{
SLInit(pcon);
}
void ContactDestroy(contact* pcon)
{
SLDestroy(pcon);
}
添加、删除、修改联系人
int ContactExist(contact* pcon, char name[])
{
assert(pcon);
for (int i = 0; i < pcon->size; i++)
{
if (!(strcmp(pcon->a[i].name, name)))
return i;
}
return -1;
}
void ContactAdd(contact* pcon)
{
CInfo con;
printf("请输入联系人的姓名:"); scanf("%s", con.name);
printf("请输入联系人的号码:"); scanf("%s", con.tel);
printf("请输入联系人的性别:"); scanf("%s", con.sex);
printf("请输入联系人的年龄:"); scanf("%d", &con.age);
printf("请输入联系人的地址:"); scanf("%s", con.addr);
SLPushBack(pcon, con);
}
void ContactDel(contact* pcon)
{
char name[NAME_MAX];
printf("请输入要删除的联系人的姓名:"); scanf("%s", name);
int x = ContactExist(pcon, name);
if ( x< 0)
{
printf("不存在该联系人。\n");
return;
}
SLErase(pcon, x);
}
void ContactModify(contact* pcon)
{
assert(pcon);
char name[NAME_MAX];
printf("请输入要修改的联系人的姓名:"); scanf("%s", name);
int x = ContactExist(pcon, name);
if (x < 0)
{
printf("不存在该联系人。\n");
return;
}
printf("请输入新的联系人的姓名:"); scanf("%s", pcon->a[x].name);
printf("请输入新的联系人的号码:"); scanf("%s", pcon->a[x].tel);
printf("请输入新的联系人的性别:"); scanf("%s", pcon->a[x].sex);
printf("请输入新的联系人的年龄:"); scanf("%d", &pcon->a[x].age);
printf("请输入新的联系人的地址:"); scanf("%s", pcon->a[x].addr);
printf("修改成功!\n");
}
通讯录的查找指定联系人
void ContactFind(contact* pcon)
{
assert(pcon);
char name[NAME_MAX];
printf("请输入要查找的联系人的姓名:"); scanf("%s", name);
int x = ContactExist(pcon, name);
if (x < 0)
{
printf("不存在该联系人。\n");
return;
}
printf("%-10s|%-10s|%-10s|%-12s|%-10s\n", "姓名", "性别", "年龄", "号码", "住址");
printf("%-10s|%-10s|%-10d|%-12s|%-10s\n", pcon->a[x].name, pcon->a[x].sex,
pcon->a[x].age, pcon->a[x].tel, pcon->a[x].addr);
}
通讯录整体功能整合
void Menu()
{
printf("*********************************************\n");
printf("***************1.添加联系人******************\n");
printf("***************2.删除联系人******************\n");
printf("***************3.查看通讯录******************\n");
printf("***************4.查找联系人******************\n");
printf("***************5.修改联系人******************\n");
printf("***************0. 退出 ******************\n");
printf("*********************************************\n");
}
int main()
{
int cho = 0;
contact con;
ContactInit(&con);
do {
Menu();
printf("请输入你的选择:"); scanf("%d", &cho);
switch (cho)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactShow(&con);
break;
case 4:
ContactFind(&con);
break;
case 5:
ContactModify(&con);
break;
case 0:
break;
default:
printf("非法输入,请重新输入:\n");
}
} while (cho != 0);
ContactDestroy(&con);
return 0;
}
·以上就是顺序表以及顺序表应用的所有内容了!