目录
一:创建源文件和头文件
头文件:SeqList.h
源文件:text.c SeqList.c
其中头文件用来定义变量,声明函数和包含一些必要的头文件,SeqList.c用来实现顺序表的一些功能(函数定义),text.c用来测试函数。
二:顺序表结构声明和初始化
(1):顺序表结构声明
我们希望顺序表可以依据我们储存的元素数量的大小来调整空间容量,因此我们需要在结构体内部声明一个我们需要储存的数据类型的指针,并声明两个int类型的变量用来记录内存中的有效数据个数和内存的大小。(这里的内存大小指的是内存中最多储存的数据的个数)
(2):结构体初始化
在SeqList.h中声明初始化函数,在SeqList.c中定义初始化函数SeqListInit()(后续函数都是如此),函数参数为结构体指针,函数返回值为void。
三:动态申请内存和释放空间
(1):动态申请内存
考虑到后续头部插入数据和尾部插入我们都需要判断是否申请内存空间,我们可以把判断并申请空间的功能封装成函数SeqListCheckCapacity(),函数参数为结构体指针,函数返回值为void。我们使用realloc()函数来进行初始空间的申请和后续内存扩容。
//判断是否申请内存
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) //判断数组个数是否和最大容量相同,是就进行扩容
{
//判断是否第一次储存数据,如果是就申请可以储存4个数据的空间
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//进行空间扩容,扩大为原先的二倍(注意强制转换realloc()函数的类型)
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->data, newcapacity * sizeof(SeqListDataType));
//如果申请空间不成功,不会释放原数组,会返回一个空地址,我们需要报错并退出程序
if (tmp == NULL)
{
printf("reallco error\n");
exit(-1);
}
//将新的内存大小赋给结构体变量
ps->capacity = newcapacity;
ps->data = tmp;
}
}
(2):释放空间
我们动态申请的内存空间如果的及时释放,可能会导致我们后续无法找到该空间地址,从而造成内存泄漏,为了避免这种情况,我们需要设计一个SeqListDestroy()函数,函数参数为结构体指针,函数返回值为void。
四:尾部插入和尾部删除
(1):尾部插入
每次进行数据插入我们都需要调用申请内存的函数,如果有效元素个数小于内存大小,内存空间足够,我们将新数据放在第(size+1)个位置,对应的元素下标为size,元素个数size加1;如果有效元素个数与内存大小相等,我们进行扩容,将新数据放在第(size+1)个位置,对应的元素下标为size,元素个数size加1。
(2):尾部删除
数组有效元素个数size是我们插入,查找,打印的依据,如果元素在size加1的位置,我们可以认为这个数据“失联”了,因此可以直接将size减1,达到伪删除的效果。使用断言函数assert(),如果size值为0,即数组中没有数据,程序报错并提示错误原因。
五:头部插入和头部删除
(1):头部插入
与尾插一样,每次头部插入前我们也需要调用申请内存的函数,但我们要的是插入到第一个位置,即下标0,我们需要把所有数据后移一位,然后插入数据,注意将size加1。
(2):头部删除
与尾部输出不同,头部删除不是让最后的数据“失联”,而是保留第一位数据后的所有有效数据,因此我们可以将第二位开始的元素都前移一个位置,从而覆盖掉第一个数据,最后size减-1。涉及到删除数据,我们也要使用断言函数assert(),如果size值为0,即数组中没有数据,程序报错并提示错误原因。
六:打印和查找
(1):打印
为了方便我们检查函数功能是否实现,我们设计一个打印函数SeqListPrintf(),它的功能是遍历数组中有效元素个数并打印。
(2):查找
因为顺序表中储存的数据并不是有序的,所有我们需要遍历数组的每一个有效数据,找到相同值并返回位置(返回下标也可以)。我们设计函数 SeqListFind(),函数第一个参数依然位结构体指针,第二个参数为数据类型,函数返回值为int。
七:指定插入和指定删除
(1):指定插入
与头部插入类似,但我们只需要将第n个位置及其后的数据后移一位,然后将新数据放在第n个位置,但我们需要注意插入的位置不能是有效数据范围外的区域。
(2):指定删除
与头部删除类似,但我们只需要将第n加1个位置及其后的数据都前移一位即可,但需要注意删除的位置不能是有效数据范围外的区域。
八:菜单和最终代码
(1):菜单
我们可以设计一个菜单,使用switch()函数,让用户使用这个顺序表。
//功能选择
int n = 0;
//插入数据
int x = 0;
//插入位置
int k = 0;
SL s1;
//开始进行初始化
SeqListInit(&s1);
while (1)
{
menu();
scanf("%d", &n);
switch (n)
{
case 1: {printf("输入数据: "); scanf("%d", &x); SeqListPushBack(&s1, x); break; }
case 2: {SeqListPopBack(&s1); break; }
case 3: {printf("输入数据: "); scanf("%d", &x); SeqListPushFront(&s1, x); break; }
case 4: {SeqListPopFront(&s1); break; }
case 5: {printf("分别输入位置和数据:"); scanf("%d %d", &k, &x); SeqListInsert(&s1, k, x); break; }
case 6: {printf("输入位置:"); scanf("%d", &k); SeqListErase(&s1, k); break; }
case 7: {printf("输入数据: "); scanf("%d", &x);SeqListFind(&s1, x); break; }
case 8: {SeqListPrintf(&s1); printf("\n"); break; }
case 9: {SeqListDestroy(&s1); SeqListInit(&s1); break; }
}
printf("\n");
}
(2):最终代码
SeqList.h
#pragma once
//三个必须的头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//重定义,方便更改数组中存储的元素类型
typedef int SeqListDataType;
//顺序表结构定义
typedef struct SeqList
{
SeqListDataType* data; //储存数据
int size; //数组中的有效元素个数
int capacity; //数组的内存大小
}SL;
//对顺序表初始化
void SeqListInit(SL* ps);
//释放空间
void SeqListDestroy(SL* ps);
//尾部插入
void SeqListPushBack(SL* ps, SeqListDataType x);
//打印
void SeqListPrintf(SL* ps);
//尾部删除
void SeqListPopBack(SL* ps);
//头部插入
void SeqListPushFront(SL* ps,SeqListDataType x);
//申请内存
void SeqListCheckCapacity(SL* ps);
//头部删除
void SeqListPopFront(SL* ps);
//查找元素
int SeqListFind(SL* ps, SeqListDataType x);
//指定插入
void SeqListInsert(SL* ps, int n, SeqListDataType x);
//删除指定位置元素
void SeqListErase(SL* ps, int n);
SeqList.c
#include "SeqList.h"
//对顺序表初始化
void SeqListInit(SL* ps)
{
ps->data = NULL;//初始化指针
ps->size = 0;
ps->capacity = 0;
}
//判断是否申请内存
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) //判断数组个数是否和最大容量相同,是就进行扩容
{
//判断是否第一次储存数据,如果是就申请可以储存4个数据的空间
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//进行空间扩容,扩大为原先的二倍(注意强制转换realloc()函数的类型)
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->data, newcapacity * sizeof(SeqListDataType));
//如果申请空间不成功,不会释放原数组,会返回一个空地址,我们需要报错并退出程序
if (tmp == NULL)
{
printf("reallco error\n");
exit(-1);
}
//将新的内存大小赋给结构体变量
ps->capacity = newcapacity;
ps->data = tmp;
}
}
//释放空间
void SeqListDestroy(SL* ps)
{
//调用free()函数释放空间
free(ps->data);
}
//尾部插入
void SeqListPushBack(SL* ps, SeqListDataType x)
{
//判断是否需要扩容
SeqListCheckCapacity(ps);
//将新数据值放第size个位置
ps->data[ps->size] =x;
//放入数据后size加1
ps->size++;
}
//尾部删除
void SeqListPopBack(SL* ps)
{
assert(ps->size > 0);
ps->size--;
}
//打印
void SeqListPrintf(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->data[i]);
}
}
//头部插入
void SeqListPushFront(SL* ps, SeqListDataType x)
{
//判断是否需要扩容
SeqListCheckCapacity(ps);
int tem = ps->size; //保留初始的size
//调用for循环,将所有数据后移一位
for (int i = 0; i < tem; i++)
{
ps->data[ps->size] = ps->data[ps->size-1];
ps->size--;
}
//插入新数据,size加1
ps->data[0] = x;
ps->size = tem + 1;
}
//头部删除
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
//把所有数据后移一位
for (int i = 0; i < ps->size; i++)
{
ps->data[i] = ps->data[i + 1];
}
//size减1
ps->size--;
}
//查找
int SeqListFind(SL* ps, SeqListDataType x)
{
//遍历数组,寻找相同值
for (int i = 0; i < ps->size; i++)
{
//查找成功返回位置
if (ps->data[i] == x)
{
printf("数据在位置%d\n", i + 1);
return 0;
}
}
printf("查找失败\n");
}
//指定插入
void SeqListInsert(SL* ps, int n, SeqListDataType x)
{
//判断插入位置是否在有效数据范围内
if (n<1 || n>ps->size)
{
printf("非法输入\n");
return;
}
//判断是否需要扩容
SeqListCheckCapacity(ps);
int tem = ps->size; //记录初始的size
for (int i = n-1; i < ps->size; i++)
{
ps->data[ps->size] = ps->data[ps->size-1];
ps->size--;
}
//修改第n个位置的值
ps->data[n - 1] = x;
//size加1
ps->size = tem + 1;
}
//指定删除
void SeqListErase(SL* ps, int n)
{
if (n<1 || n>ps->size)
{
printf("非法输入\n");
return;
}
for (int i = n - 1; i < ps->size; i++)
{
//如果要删除的元素是最后一个元素,就不进行覆盖,跳出循环。
if (n == ps->size)
break;
ps->data[n-1] = ps->data[n];
n++;
}
//size减1
ps->size--;
}
text.c
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void menu()
{
printf("**************************\n");
printf("***1.尾插2.尾删***********\n");
printf("***3.头插4.头删***********\n");
printf("***5.指定插入6.指定删除***\n");
printf("***7.查找8.打印9.清空*****\n");
printf("******************请输入-》");
}
int main()
{
//功能选择
int n = 0;
//插入数据
int x = 0;
//插入位置
int k = 0;
SL s1;
//开始进行初始化
SeqListInit(&s1);
while (1)
{
menu();
scanf("%d", &n);
switch (n)
{
case 1: {printf("输入数据: "); scanf("%d", &x); SeqListPushBack(&s1, x); break; }
case 2: {SeqListPopBack(&s1); break; }
case 3: {printf("输入数据: "); scanf("%d", &x); SeqListPushFront(&s1, x); break; }
case 4: {SeqListPopFront(&s1); break; }
case 5: {printf("分别输入位置和数据:"); scanf("%d %d", &k, &x); SeqListInsert(&s1, k, x); break; }
case 6: {printf("输入位置:"); scanf("%d", &k); SeqListErase(&s1, k); break; }
case 7: {printf("输入数据: "); scanf("%d", &x);SeqListFind(&s1, x); break; }
case 8: {SeqListPrintf(&s1); printf("\n"); break; }
case 9: {SeqListDestroy(&s1); SeqListInit(&s1); break; }
}
printf("\n");
}
//结束及时释放空间
SeqListDestroy(&s1);
return 0;
}