赞
踩
//使用宏定义数据类型,方便修改
#define SLDateType int
typedef struct SeqList
{
SLDateType arr[10];//定长数组
int size;//有效数据的个数
}SL;
注意:静态顺序表存在一定的缺陷,空间给少了造成数据丢失,空间给多了造成空间浪费。
//使用宏定义数据类型,方便修改
#define SLDateType int
typedef struct SeqList
{
SLDateType* arr;//大小可以动态变化
int size;//有效数据的个数
int capacity;//空间容量(即申请空间的总大小)
}SL;
注意:动态顺序表可以进行扩容,一般为原来容量的2~3倍。
为了实现动态顺序表,我们可以通过多文件的方式,让代码的可读性更高。
在这里我们使用两个文件 SL.h 头文件,用于声明顺序表(结构体)以及相关函数,和 SeqList.c 文件,用于实现相关函数。
文件 SL.h :
#include<stdio.h> #include<stdlib.h> #include<assert.h> #define SLDataType int typedef struct SeqList { SLDataType* arr;//大小可以动态变化 int size;//有效数据的个数 int capacity;//总空间的大小 }SL; //函数部分 void SLInit(SL* ps); void SLCheckCapacity(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 SLErease(SL* ps, int pos); void SLDestroy(SL* ps);
我们可以在头文件中包含头文件,方便后续测试。
注意:使用 SLDataType 来替换 int ,可以让代码适用于各种数据类型,例如整型、浮点型、结构体类型等,方便后期的修改。
文件 SeqList.c 函数讲解:
顺序表的初始化:
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
顺序表的空间检查:
void SLCheckCapacity(SL* ps) { int NewCapacity = 0; if (ps->size == ps->capacity) { if (ps->capacity == 0)//这是刚初始化完成,在这里给他赋一个初值,要不然NewCapacity一直是0 { NewCapacity = 4; } else//一般情况下,二倍扩容 { NewCapacity = 2 * (ps->capacity); } //使用函数申请空间 SLDataType* ptr = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType)); assert(ptr != NULL); ps->arr = ptr; ps->capacity = NewCapacity; ptr = NULL; } }
当我们想要插入元素时,必须要检查该顺序表的空间是否足够,防止越界。
每执行一次函数功能,仅插入一个元素,因此不用考虑扩容后是否可以容纳全部元素。扩容会涉及到使用函数realloc( ),因此我们定义一个新的变量NewCapacity(包括后续的ptr),防止发生意外来影响我们原先的数据。
当我们第一次给顺序表中添加元素时,由于size和capacity均为0,我们可以给NewCapacity一个处初值4,后续通常扩容原本空间大小的2~4倍。
顺序表的尾插与头插:
//尾插 void SLPushBack(SL* ps, SLDataType x) { assert(ps); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 ps->arr[ps->size] = x; (ps->size)++;//记住size的值要发生变化 } //头插 void SLPushFront(SL* ps, SLDataType x) { assert(ps != NULL); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 //将数据从后向前依次往后移动一位 for (int i = 0; i < ps->size; i++) { ps->arr[ps->size - i] = ps->arr[ps->size - 1 - i]; } ps->arr[0] = x; (ps->size)++; }
需要注意size的变化和数据移动的问题,头插过程中,数据是从后向前,依次向后移动一位,这样可以避免数据被覆盖。
顺序表的尾删与头删:
//尾删 void SLPopBack(SL* ps) { assert(ps != NULL); assert(ps->size != 0);//该顺序表至少存在一个元素 (ps->size)--;//直接改变size即可,反正没有赋值的元素是随机值 } //头删 void SLPopFront(SL* ps) { assert(ps != NULL); assert(ps->size != 0);//该顺序表至少存在一个元素 //将数据从前向后依次向前移动一位 for (int i = 1; i < ps->size; i++) { ps->arr[i - 1] = ps->arr[i]; } (ps->size)--; }
尾删直接让size减一即可,头删需要让数据从前向后,依次向前移动一位。
指定位置删除与销毁:
//在指定位置插入数据 void SLInsert(SL* ps, int pos, SLDataType x) { assert(ps != NULL); assert(pos < ps->size && pos >= 0); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 for (int i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[pos] = x; (ps->size)++; } //在指定位置删除数据 void SLErease(SL* ps, int pos) { assert(ps != NULL); assert(pos < ps->size && pos >= 0); //将数据从前往后依次向前移动一个单位 for (int i = 0; i < (ps->size - pos); i++) { ps->arr[pos + i] = ps->arr[pos + i + 1]; } (ps->size)--; }
与之前所述的插入与删除相同,通过分析我们可以简单得出数据移动的方向。
顺序表的销毁:
//顺序表的销毁
void SLDestroy(SL* ps)
{
assert(ps != NULL);
if (ps->arr != NULL)
{
free(ps->arr);
ps->arr = NULL;
}
ps->size = ps->capacity = 0;
}
动态内存获取的数据如果不销毁,就会造成内存泄漏。
//顺序表的初始化 void SLInit(SL* ps) { ps->arr = NULL; ps->capacity = ps->size = 0; } //顺序表的空间检查——在修改数据之前,应当先检查空间 void SLCheckCapacity(SL* ps) { int NewCapacity = 0; //当元素个数和最大容量相等时,想要插入元素必须扩容 //一次检查,仅仅插入一个元素,不用考虑插入元素的个数,多个元素循环插入 if (ps->size == ps->capacity) { if (ps->capacity == 0)//这是刚初始化完成,在这里给他赋一个初值,要不然NewCapacity一直是0 { NewCapacity = 4; } else//一般情况下,二倍扩容 { NewCapacity = 2 * (ps->capacity); } //使用函数申请空间 SLDataType* ptr = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType)); assert(ptr != NULL); ps->arr = ptr; ps->capacity = NewCapacity; ptr = NULL; } } //尾插插入数据 void SLPushBack(SL* ps, SLDataType x) { assert(ps); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 ps->arr[ps->size] = x; (ps->size)++;//记住size的值要发生变化 } //头插插入数据 void SLPushFront(SL* ps, SLDataType x) { assert(ps != NULL); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 //将数据从后向前依次往后移动一位 for (int i = 0; i < ps->size; i++) { ps->arr[ps->size - i] = ps->arr[ps->size - 1 - i]; } ps->arr[0] = x; (ps->size)++; } //尾删删除数据 void SLPopBack(SL* ps) { assert(ps != NULL); assert(ps->size != 0);//该顺序表至少存在一个元素 (ps->size)--;//直接改变size即可,反正没有赋值的元素是随机值 } //头删删除数据 void SLPopFront(SL* ps) { assert(ps != NULL); assert(ps->size != 0);//该顺序表至少存在一个元素 //将数据从前向后依次向前移动一位 for (int i = 1; i < ps->size; i++) { ps->arr[i - 1] = ps->arr[i]; } (ps->size)--; } //在指定位置插入数据 void SLInsert(SL* ps, int pos, SLDataType x) { assert(ps != NULL); assert(pos < ps->size && pos >= 0); SLCheckCapacity(ps);//检查并且修改空间,之后空间条件满足 for (int i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[pos] = x; (ps->size)++; } //在指定位置删除数据 void SLErease(SL* ps, int pos) { assert(ps != NULL); assert(pos < ps->size && pos >= 0); //将数据从前往后依次向前移动一个单位 for (int i = 0; i < (ps->size - pos); i++) { ps->arr[pos + i] = ps->arr[pos + i + 1]; } (ps->size)--; } //顺序表的销毁 void SLDestroy(SL* ps) { assert(ps != NULL); if (ps->arr != NULL) { free(ps->arr); ps->arr = NULL; } ps->size = ps->capacity = 0; }
通过上述动态顺序表的实现,我们已经获得了两个文件:SL.h 和 SeqList.c 文件,接下来我们就可以使用这两个文件来实现通讯录项目。
由于联系人是一个复杂对象,因此我们需要用到自定义类型——结构体去实现。
再头文件 contact.h 中创造一个联系人结构体,用于储存联系人的信息:
//定义一个联系人的结构体
#define NAME_MAX 20
#define TEL_MAX 20
typedef struct personInfo
{
char name[NAME_MAX];
char tel[TEL_MAX];
}pInfo;
typedef struct Seqlist Contant;//将原本的数据表改为通讯录,更加直观
注意:该处使用代码:typedef struct Seqlist Contant,而不使用:typrdef SL Contant。
#include<stdio.h> typedef struct SeqList Contact; void Fun(Contact* s) { printf("struct"); } struct SeqList { int* arr; int size; int capacity; }; int main() { Contact* C1 = { 0 }; Fun(C1); return 0; }
函数Fun( ) 可以成功执行。
我们在声明结构体前使用typedef给结构体重命名,这就是结构体的提前声明,对应到通讯录中也是一样:
//头文件Contac.h内容 typedef struct personInfo { //... }pInfo; typedef struct SeqList Contact; //声明部分 void ConInit(Contact* con); //函数声明部分 //头文件SeqList.h头文件 #include"Contact.h" typedef pInfo SLdt; typedef struct SeqList //定义部分 { //... }SL; void SLInit(SL* ps);
Contact.h中函数的实现:
我们将这些函数的实现封装在文件 ContactFun.c 文件中。
//初始化数据
int R = 0;//仅用于接受scanf函数的返回值
void ConInit(Contact* con)
{
assert(con != NULL);
SLInit(con);
}
//销毁数据
void ConDestory(Contact* con)
{
SLDestory(con);
}
注意:以下函数出现的全局变量R,仅用来接受scanf( ) 函数的返回值。
//增加联系人(name+tel) void ConAdd(Contact* con) { pInfo In = { 0 }; //用户手动输入以下信息 printf("请输入你想要添加的联系人的个数:"); int num = 0; R = scanf("%d", &num); assert(num >= 1); for (int i = 1; i <= num; i++) { printf("输入第%d个要添加联系人的名字和电话:\n",i); R = scanf("%s %s", In.name,In.tel); //用SL中函数添加信息 SLPushBack(con, In); } printf("%d个联系人已添加完毕\n",num); }
我们在之前定义了pInfo结构变量,用于储存联系人的信息。
使用SeqList中的尾插方法来添加联系人。
//联系人的查找
int FindName(Contact* con)
{
char nameF[NAME_MAX] = { 0 };
R = scanf("%s", nameF);
for (int i = 0; i < con->size; i++)
{
if (strcmp(nameF, con->arr[i].name) == 0)
{
return i;
}
}
return -1;
}
该函数用来寻找联系人的ID,和判断是否存在该联系人。使用函数strcmp( ) 函数比较两个字符串是否相等,若存在相等的字符串则返回该联系人在数组中的ID,否则返回-1表示不存在。便于后续函数功能的实现。
void ConChange(Contact* con) { //输入需要修改的联系人姓名 printf("输入想要修改的联系人姓名:"); int ret = FindName(con); if (ret == -1) { printf("未发现该联系人\n"); } else//已经发现了,该元素的ID就是ret { printf("输入修改后的名字与电话:"); R = scanf("%s %s", con->arr[ret].name, con->arr[ret].tel); printf("修改成功\n"); return; } }
利用了上述联系人ID查找函数FindName( ) ,找到想要修改的联系人后,输入新的名字与电话覆盖即可。
注意:在代码中,Con就表示我们的SeqList结构体(改名后为Contact),通过SeqList的结构体指针找到对应储存联系人信息的数组(即SLdt* arr),找到对应的ID(即arr [ ret ] ),访问得到数组pInfo结构体的元素,即可修改。
//删除联系人 void ConDeleat(Contact* con) { printf("输入想要删除的联系人姓名:"); int ret = FindName(con); if (ret == -1) { printf("未发现该联系人\n"); } else//已经发现了,该元素的ID就是ret { SLErease(con, ret); printf("删除成功\n"); return; } }
使用SeqList.h文件中的删除元素的函数即可。
//展示联系人
void ConShow(Contact* con)
{
for (int i = 0; i < con->size; i++)
{
printf("姓名:%s 电话:%s\n", con->arr[i].name, con->arr[i].tel);
}
printf("已展示全部联系人\n");
}
//查找联系人是否存在
void ConFind2(Contact* con)
{
int m = FindName(con);
if (m >= 0)
{
printf("输入你要查找联系人的姓名:\n");
printf("发现该联系人:\n");
printf("姓名:%s 电话:%s\n", con->arr[m].name, con->arr[m].tel);
}
else
{
printf("未发现该联系人\n");
}
}
至此,ContactFun.c 文件中的主要函数就写完了。
下面我们通过main( ) 函数中的程序实现该通讯录:
为了方便实验,我们在SL.h 头文件中编写菜单函数menu( ) :
void menu()
{
printf("****************************\n");
printf("1.增加联系人 2.删除联系人\n");
printf("3.修改联系人 4.查找联系人\n");
printf("5.展示联系人 0.退出通讯录\n");
printf("****************************\n");
}
由于我们在SL.h头文件中包含过Contact.h等文件,在主函数文件中包含一个头文件即可。
#include"SL.h" int main() { int R = 0;//仅用于接受scanf函数的返回值 Contact C = { 0 }; int x = 0; do { menu(); printf("输入你所需要的功能:"); R = scanf("%d", &x); switch (x) { case 1: ConAdd(&C); break; case 2: ConDeleat(&C); break; case 3: ConChange(&C); case 4: ConFind2(&C); break; case 5: ConShow(&C); break; case 0: ConDestory(&C); break; default: printf("输入错误,请重新输入\n"); } } while (x); return 0; }
#pragma once //Seqlist.h //可用一个头文件包含多个头文件 #include"Contact.h" #include<stdio.h> #include<assert.h> #include<stdlib.h> #include<string.h> typedef pInfo SLdt; typedef struct SeqList { SLdt* arr; int size; int capacity; }SL; void SLInit(SL* ps); void SLCheckCapacity(SL* ps); void SLPushBack(SL* ps, SLdt x); void SLPushFront(SL* ps, SLdt x); void SLPopBack(SL* ps); void SLPopFront(SL* ps); void SLErease(SL* ps, int pos); void SLInsert(SL* ps, int pos, SLdt x); void SLDestory(SL* ps);
#pragma once //定义一个联系人的结构体 #define NAME_MAX 20 #define TEL_MAX 20 typedef struct personInfo { char name[NAME_MAX]; char tel[TEL_MAX]; }pInfo; typedef struct SeqList Contact; void ConInit(Contact* con); void ConAdd(Contact* con); int FindName(Contact* con); void ConFind2(Contact* con); void ConChange(Contact* con); void ConDeleat(Contact* con); void ConShow(Contact* con); void ConDestory(Contact* con); void menu();
#include<assert.h> #include<stdio.h> #include<stdlib.h> #include "SL.h" //顺序表的初始化 void SLInit(SL* ps) { //assert(ps != NULL); ps->arr = NULL; ps->capacity = 0;//空间总个数 ps->size = 0;//元素个数 } //空间的检查与申请 void SLCheckCapacity(SL* ps) { int NewCapacity = 0; if (ps->capacity == ps->size)//相等则表示内存不够了 { if (ps->capacity == 0)//刚初始化完成的情况 NewCapacity = 4; else NewCapacity = (ps->capacity) * 2;//一般情况 SLdt* ptr = (SLdt*)realloc(ps->arr, NewCapacity * sizeof(SLdt));//申请内存 assert(ptr != NULL); ps->arr = ptr; ps->capacity = NewCapacity; ptr = NULL; } } //元素的尾插 void SLPushBack(SL* ps, SLdt x) { assert(ps != NULL); SLCheckCapacity(ps);//检查内存 ps->arr[ps->size] = x; (ps->size)++; } //元素的头插 void SLPushFront(SL* ps, SLdt x) { assert(ps != NULL); SLCheckCapacity(ps);//检查内存 //元素从后向前,依次向后移动一位 for (int i = 0; i < ps->size; i++) { ps->arr[ps->size - i] = ps->arr[ps->size - 1 - i]; } ps->arr[0] = x; (ps->size)++; } //元素的尾删 void SLPopBack(SL* ps) { assert(ps != NULL); (ps->size)--; } //元素的头删 void SLPopFront(SL* ps) { assert(ps != NULL); assert(ps->size != 0); //所有元素从前向后,依次向前移动一位 for (int i = 1; i < ps->size; i++) { ps->arr[i - 1] = ps->arr[i]; } (ps->size)--; } //指定位置删除数据 void SLErease(SL* ps, int pos) { assert(ps != NULL); assert(ps->size != 0); assert(pos >= 0 && pos < ps->size); //该数据之后的数据,从前往后,依次向前移动一位 for (int i = pos; i < ps->size; i++) { ps->arr[i] = ps->arr[i + 1]; } (ps->size)--; } //指定位置插入数据 void SLInsert(SL* ps, int pos, SLdt x) { assert(ps != NULL); assert(pos >= 0 && pos < ps->size); assert(ps->size != 0); //该数据之后的数据,从后向前,依次向后移动一位 for (int i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[pos] = x; (ps->size)++; } //顺序表的销毁 void SLDestory(SL* ps) { assert(ps != NULL); free(ps->arr); ps->arr = NULL; ps->capacity = ps->size = 0; }
#include"SL.h" //增加联系人、删除联系人、修改联系人、查找联系人、展示联系人 //初始化数据 int R = 0;//仅用于接受scanf函数的返回值 void ConInit(Contact* con) { assert(con != NULL); SLInit(con); } //增加联系人(name+tel) void ConAdd(Contact* con) { pInfo In = { 0 }; //用户手动输入以下信息 printf("请输入你想要添加的联系人的个数:"); int num = 0; R = scanf("%d", &num); assert(num >= 1); for (int i = 1; i <= num; i++) { printf("输入第%d个要添加联系人的名字和电话:\n",i); R = scanf("%s %s", In.name,In.tel); //用SL中函数添加信息 SLPushBack(con, In); } printf("%d个联系人已添加完毕\n",num); } //联系人的查找 int FindName(Contact* con)//该函数仅用来找ID { char nameF[NAME_MAX] = { 0 }; R = scanf("%s", nameF); for (int i = 0; i < con->size; i++) { if (strcmp(nameF, con->arr[i].name) == 0) { return i; } } return -1; } //修改联系人 void ConChange(Contact* con) { //输入需要修改的联系人姓名 printf("输入想要修改的联系人姓名:"); int ret = FindName(con); if (ret == -1) { printf("未发现该联系人\n"); } else//已经发现了,该元素的ID就是ret { printf("输入修改后的名字与电话:"); R = scanf("%s %s", con->arr[ret].name, con->arr[ret].tel); printf("修改成功\n"); return; } } //删除联系人 void ConDeleat(Contact* con) { printf("输入想要删除的联系人姓名:"); int ret = FindName(con); if (ret == -1) { printf("未发现该联系人\n"); } else//已经发现了,该元素的ID就是ret { SLErease(con, ret); printf("删除成功\n"); return; } } //展示联系人 void ConShow(Contact* con) { for (int i = 0; i < con->size; i++) { printf("姓名:%s 电话:%s\n", con->arr[i].name, con->arr[i].tel); } printf("已展示全部联系人\n"); } //查找联系人是否存在 void ConFind2(Contact* con) { int m = FindName(con); if (m >= 0) { printf("输入你要查找联系人的姓名:\n"); printf("发现该联系人:\n"); printf("姓名:%s 电话:%s\n", con->arr[m].name, con->arr[m].tel); } else { printf("未发现该联系人\n"); } } //销毁数据 void ConDestory(Contact* con) { SLDestory(con); } //菜单 void menu() { printf("****************************\n"); printf("1.增加联系人 2.删除联系人\n"); printf("3.修改联系人 4.查找联系人\n"); printf("5.展示联系人 0.退出通讯录\n"); printf("****************************\n"); }
#include"SL.h" int main() { int R = 0;//仅用于接受scanf函数的返回值 Contact C = { 0 }; int x = 0; do { menu(); printf("输入你所需要的功能:"); R = scanf("%d", &x); switch (x) { case 1: ConAdd(&C); break; case 2: ConDeleat(&C); break; case 3: ConChange(&C); case 4: ConFind2(&C); break; case 5: ConShow(&C); break; case 0: ConDestory(&C); break; default: printf("输入错误,请重新输入\n"); } } while (x); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。