顺序表采用模块化编程思路,顺序表的实现使用3个模块,test.c—测试模块 Seqlist.c—接口函数的实现模块 seqlist.h—接口函数声明
顺序表是在计算机内存中通常以数组形式存储的线性表,线性表是n个具有相同特性的数据元素的有限序列,线性表的顺序存储是用一组地址连续的存储单元依次存储线性表中的各个元素;采用顺序存储结构的线性表通常称为顺序表,顺序表是将表中的节点依次存放在计算机内存中一组地址连续的存储单元之中;
静态顺序表:使用定长数组存储元素;
//静态顺序表
# define N 100
typedef int SeqlistDatatye;
typedef struct Seqlist
{
SeqlistDatatye nums[N];
int size;//记录有效数据的个数
}Seqlist;
静态顺序表的缺点:当定义容量的N太小,空间不够使用,当N过大,浪费多余的空间;
为解决这种缺点,创建了动态顺序表,可以根据需要分配空间的大小;
动态顺序表:使用动态开辟的数组存储;
//动态顺序表
typedef int SLDataType;
typedef struct Seqlist
{
SLDataType* nums;//nums指向动态开辟的数组
int size;//记录有效数据的个数
int capacity;//存储有效数据的容量大小,单位为每个结构体的大小
}Seqlist;
如图所示:
动态顺序表的优点:动态顺序表的容量由动态内存函数所开辟,当容量不够使用时可以增加容量capacity, 扩容方法是利用realloc()函数动态开辟一块新的空间(新空间的大小一般为原空间的两倍),若为本地扩容,直接返回原先空间的起始地址,若为异地扩容,首先将旧空间的数据拷贝到新的空间,其次释放旧的空间,最后返回新空间的起始地址;
//test.c文件
void Test()
{
Seqlist SL;//创建顺序表
InitSeqlist(&SL);//初始化顺序表
}
int main()
{
Test();
return 0;
}
//Seqlist.c文件
//初始化顺序表
void InitSeqlist(Seqlist* ps)
{
assert(ps != NULL);
//开辟一定容量的空间 (*ps).nums成员类型为SLDataType*,malloc返回值类型为void*,所以发生强转
ps->nums = (SLDataType*)malloc(4 * sizeof(SLDataType*));
//判断空间开辟是否成功
if (ps->nums == NULL)
{
perror("malloc");
exit(-1);
//exit()函数 exit(0)表示正常退出,exit(x),x不为0表示异常退出,终止正在执行的进程;
}
//空间开辟成功
ps->size = 0;
ps->capacity = 4;//单位大小为sizeof(SLDataType*)
}
调试窗口:
//Seqlist.c文件
//顺序表的销毁
void DestroySeqlist(Seqlist* ps)
{
assert(ps != NULL);
free(ps->nums);
ps->nums = NULL;
ps->size = 0;
ps->capacity = 0;
}
//Seqlist.c文件
void printSeqlist(Seqlist* ps)
{
assert(ps != NULL);
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->nums[i]);
}
printf("\n");
}
//Seqlist.c文件
void Seqlistpushback(Seqlist* ps, SLDataType x)
{
assert(ps != NULL);
//首先应该判断顺序表是否已满,若空间已满,进行扩容;
//当ps->size=ps->capacity说明空间已满
if (ps->capacity == ps->size)
{
//用realloc()函数进行扩容,出现两种情况 1.同地扩容 2.异地扩容
//必须新建变量接收扩容后的地址变量;
SLDataType* tmp = (SLDataType*)realloc(ps->nums, (ps->capacity) * 2 * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
ps->capacity = (ps->capacity) * 2;
ps->nums = tmp;
}
//扩容成功,尾插元素
//总共有ps->size个有效数据,数组下标范围为0到(ps->size)-1;
ps->nums[ps->size] = x;
ps->size++;
}
void Test1()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test1();
return 0;
}
运行结果:
void Seqlistpopback(Seqlist* ps)
{
assert(ps != NULL);
//首先检查顺序表中是否可以有元素删除,若不做检查,可能导致指针越界访问;
//检查方式一
/*if (ps->size == 0)
{
return;
}*/
//检查方式二
assert(ps->size > 0);
//尾删元素
ps->size--;
}
//尾删元素测试
void Test2()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
Seqlistpopback(&SL);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test2();
return 0;
}
运行结果:
当顺序表头插和尾插元素时,需要检查空间是否足够使用,若空间不够,需要进行扩容,为使代码不要冗余,单独封装为Checkcapacity()函数,方便使用;
void CheckCapacity(Seqlist* ps)
{
assert(ps != NULL);
//首先应该判断顺序表是否已满,若空间已满,进行扩容;
//当ps->size=ps->capacity说明空间已满
if (ps->capacity == ps->size)
{
//用realloc()函数进行扩容,出现两种情况 1.本地扩容 2.异地扩容
//必须新建变量接收扩容后的地址变量;
SLDataType* tmp = (SLDataType*)realloc(ps->nums, (ps->capacity) * 2 * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
ps->capacity = (ps->capacity) * 2;
ps->nums = tmp;
}
}
void Seqlistpushfront(Seqlist* ps, SLDataType x)
{
assert(ps != NULL);
CheckCapacity(ps);
//从后向前依次拷贝数据,直至首元素
int end = ps->size - 1;
while (end >= 0)
{
ps->nums[end + 1] = ps->nums[end];
--end;
}
//插入元素
ps->nums[0] = x;
ps->size++;
}
//头插元素测试
void Test3()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushfront(&SL, 1);
Seqlistpushfront(&SL, 2);
Seqlistpushfront(&SL, 3);
Seqlistpushfront(&SL, 4);
Seqlistpushfront(&SL, 5);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main ()
{
Test3();
return 0;
}
运行结果如下:
//顺序表头删元素
void Seqlistpopfront(Seqlist* ps)
{
assert(ps != NULL);
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->nums[begin - 1] = ps->nums[begin];
++begin;
}
ps->size--;
}
void Test4()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
Seqlistpopfront(&SL);
printSeqlist(&SL);
Seqlistpopfront(&SL);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test4();
return 0;
}
运行结果:
void SeqlistInsert(Seqlist* ps, int pos, SLDataType x)
{
assert(ps != NULL);
assert(pos >= 0 && pos <= ps->size);
CheckCapacity(ps);
//从顺序表最后一个元素向前拷贝,向后移动,直到pos位置为止
int end = ps->size - 1;
while (end >= pos)
{
ps->nums[end + 1] = ps->nums[end];
end--;
}
ps->nums[pos] = x;
ps->size++;
}
void Test5()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
SeqlistInsert(&SL, 3, 20);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test5();
return 0;
}
运行结果:
//顺序表查找某个元素(查找到该元素返回下标,查找不到返回-1)
int SeqlistFind(Seqlist* ps, SLDataType x)
{
assert(ps != NULL);
int j = 0;
for (j = 0; j < ps->size; j++)
{
if (ps->nums[j] == x)
{
return j;
break;
}
}
//查找不到该元素
return -1;
}
void Test6()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
int x;
printf("请输入要查找的元素\n");
scanf("%d", &x);
int pos = SeqlistFind(&SL, x);
if (pos != -1)
{
SeqlistInsert(&SL, pos, x * 10);
printSeqlist(&SL);
}
else
{
printf("查找的元素不存在\n");
}
DestroySeqlist(&SL);
}
int main()
{
Test6();
return 0;
}
运行结果:
void SeqlistErase(Seqlist* ps, int pos)
{
assert(ps != NULL);
//首先判断pos位置下标是否合法,避免数组越界访问
assert(pos >= 0 && pos < ps->size);
//坐标合法,从pos下一个位置从前向后拷贝
int begin = pos + 1;
while (beginsize)
{
ps->nums[begin - 1] = ps->nums[begin];
begin++;
}
ps->size--;
}
void Test7()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
int x;
printf("请输入要删除的元素\n");
scanf("%d", &x);
int pos = SeqlistFind(&SL, x);
if (pos != -1)
{
SeqlistErase(&SL, pos);
printSeqlist(&SL);
}
else
{
printf("删除的元素不存在\n");
}
DestroySeqlist(&SL);
}
int main()
{
Test7();
return 0;
}
运行结果:
利用SeqlistInsert()函数改造头插 ,尾插函数;
//头插函数改造版
void SeqlistPushfront(Seqlist* ps, SLDataType x)
{
SeqlistInsert(ps, 0, x);
}
void Test8()
{
Seqlist SL;
InitSeqlist(&SL);
SeqlistPushfront(&SL, 10);
SeqlistPushfront(&SL, 20);
SeqlistPushfront(&SL, 30);
SeqlistPushfront(&SL, 40);
SeqlistPushfront(&SL, 50);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test8();
return 0;
}
运行结果:
void SeqlistPushback(Seqlist* ps, SLDataType x)
{
SeqlistInsert(ps, ps->size, x);
}
void Test9()
{
Seqlist SL;
InitSeqlist(&SL);
SeqlistPushback(&SL, 11);
SeqlistPushback(&SL, 12);
SeqlistPushback(&SL, 13);
SeqlistPushback(&SL, 14);
SeqlistPushback(&SL, 15);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test9();
return 0;
}
运行结果:
利用SeqlistErase()函数改造头删 尾删函数;
void SeqlistPopfront(Seqlist* ps)
{
SeqlistErase(ps, 0);
}
void Test10()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
SeqlistPopfront(&SL);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test10();
return 0;
}
运行结果:
void Test11()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
SeqlistPopback(&SL);
printSeqlist(&SL);
SeqlistPopback(&SL);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test11();
return 0;
}
void SeqlistModify(Seqlist* ps, int pos, SLDataType x)
{
assert(ps != NULL);
//检查要修改某个元素的下标是否合法
assert(pos >= 0 && pos < ps->size - 1);
ps->nums[pos] = x;
}
void Test12()
{
Seqlist SL;
InitSeqlist(&SL);
Seqlistpushback(&SL, 1);
Seqlistpushback(&SL, 2);
Seqlistpushback(&SL, 3);
Seqlistpushback(&SL, 4);
Seqlistpushback(&SL, 5);
printSeqlist(&SL);
SeqlistModify(&SL, 2, 100);
printSeqlist(&SL);
SeqlistModify(&SL, 2, 200);
printSeqlist(&SL);
DestroySeqlist(&SL);
}
int main()
{
Test12();
return 0;
}
运行结果: