顺序表是指把数据放在一个地址连续的存储单元里。
用c语言实现的顺序表要掌握数组、结构体、指针、函数调用的知识。顺序表由存储大小是否固定可分为静态和动态。静态顺序表,首先建立一个固定大小的数组a[N]来存储数据,同时还需要定义一个size尺寸来实时显示当前数据的数量,仅仅由这两个成员就可以组建一个顺序表的基本结构体。而动态顺序表结构体中利用一个数组指针来实现更新数组的大小。
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType a[N];
int size; //表示数组中存储了多少个数据
}SL;
typedef struct SeqList
{
SLDataType* a; //指针指向动态开辟的空间(数组名)
int size; //表示数组中存储了多少个数据
int capacity; //数组实际能存放数据的空间
}SL;
接下来是实现数据结构基本功能的接口函数,例如,你想要添加、删除、查找、输出数据等。
首先对结构体进行初始化,可以尝试把初始化内容放在一个自建函数里,这样美观整齐且方便调用。
void SeqListInit(SL* ps) //顺序表的初始化
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
对于插入数据接口,我们可以分类讨论,如:头插、尾插、指定位置插。其中指定位置插包括了前两种情况。插入数据,首先就要讨论空间是否足够问题,一开始没有数据时空间为0,通过条件运算可以赋予初始空间大小,值得注意的是realloc增容的单位为字节。尾插比较简单,直接在后面加入数据就行,别忘了最后要size++。头插要考虑到数组的数据位置连续问题,首先从数组的最后一位开始一位接着一位往后移,这样避免了数据被覆盖,最后在数组下标为0的位置插入数据
void SeqListCheckCapacity(SL* ps) //扩容
{
//如果空间不足或者没有空间时,就扩容
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf(" SeqListPushBack realloc fall !\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
void SeqListPushBack(SL* ps, SLDataType x) //尾插
{
//如果空间不足或者没有空间时,就扩容
/*SeqListCheckCapacity(ps); //扩容
//表示空间足够时直接在后面加数据
ps->a[ps->size] = x;
ps->size++;
*/
//使用插入函数包含尾插
SeqListInsert(ps, ps->size, x);
}
void SeqListPushFront(SL* ps, SLDataType x) //头插
{
//如果空间不足或者没有空间时,就扩容
/*
SeqListCheckCapacity(ps); //扩容
//从尾部开始挪动数据位置
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
*/
SeqListInsert(ps, 0, x);
}
指定位置插入数据,首先要考虑到该位置pos是否存在,然后把pos和pos后面的数据和头插方式一样往后移,最后就可以在pos位置插入数据了。
void SeqListInsert(SL* ps, int pos, SLDataType x) //指定下标位置插入
{
//插入数据要考虑顺序表数据存储是连续的
if (pos > ps->size || pos < 0)
{
printf("pos invalid!\n");
return;
}
//assert(pos < =ps->size && pos >= 0);
SeqListCheckCapacity(ps); //增容
//挪动数据
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
接下来是删除接口函数,首先要判断是否有数据。尾删直接删掉最后一个数据,头删就是把后面的数据一位一位往前面覆盖,指定位置删除同理。
void SeqListPopBack(SL* ps) //尾删
{
/*assert(ps->size);
ps->size--;*/
/*if (ps->size > 0)
{
ps->size--;
}*/
SeqListErase(ps, ps->size-1); //删除指定下标位置
}
void SeqListPopFront(SL* ps) //头删
{
//判断是否有数据
/*
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
*/
SeqListErase(ps, 0); //删除指定下标位置
}
void SeqListErase(SL* ps, int pos) //删除指定下标位置
{
assert(pos < ps->size && pos >= 0);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
然后就是查找数据位置韩式接口,找到就返回下标。同理首先要判断是否有数据,然后可以用最直接的方法,挨个进行比较。
int SeqListFind(SL* ps, SLDataType x) //查找位置
{
assert(ps->size > 0);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
打印接口函数同理,用最直接方法
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
最后就是销毁空间,因为这些空间都是动态申请的,用完最后销毁有利于减小空间浪费。
void SeqListDestory(SL* ps) //销毁顺序表
{
free(ps->a);
ps->a == NULL;
ps->size = ps->capacity = 0;
}