顺序表介绍
顺序表是用来连续存放数据单元的一种结构,逻辑连续的两个元素在实际上也为连续的。
顺序表的实现
顺序标定义
由于我们存储多中数据,所以顺序表的定义使用结构体类型。
由于是连续的数据单元,所以我们使用数组来接收顺序表中的数据,size用来表示数据表中存放了多少个数组,capicity用来表示顺序表的容量。
typedef int Seqdata;
typedef struct SeqList
{
Seqdata *data;
int size;
int capicity;
}SL;
顺序表功能的实现
顺序表的功能一定有最基础的增删查改,所以接下来就来实现这些功能。
顺序表初始化
顺序表的初始化,首先断言防止传空指针,然后就需要进行开辟空间,开辟的大小由自己来定,判断是否会开辟失败,如果成功将其capicity赋值开辟的大小,并将size初始化为0,data赋值开辟的空间地址。
void SeqInit(SL* ps)
{
assert(ps);
Seqdata* tmp = (Seqdata*)malloc(sizeof(Seqdata)*4);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
ps->capicity = 4;
ps->size = 0;
ps->data = tmp;
}
顺序表的扩容
我们在使用顺序表的时候,进行增的操作时,我们就需要检查空间是否已经满了,既然我们需要经常进行这个操作,所以我们将其封装为一个函数。
如果capicity和size相同时,那么就说明空间已经满了,需要进行增容,增容使用realloc函数,增容我们任然需要判断是否增容成功,增容成功后我们需要将capicity重新修改为增容后的大小,由于realloc的特性,如果原地址不够增容,会重新在新的地址进行操作,所以以防万一我们将data赋值扩容的地址。
void check(SL* ps)
{
assert(ps);
if (ps->capicity == ps->size)
{
Seqdata* tmp = realloc(ps->data, ps->capicity * sizeof(Seqdata) * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->capicity *= 2;
ps->data = tmp;
}
return;
}
顺序表的尾增
顺序表的尾增,顾名思义就是在数组为尾部后进行插入,我们需要首先检查空间是否已满,直接将size当作下标赋值,最后记得size++。
void Pushtail(SL* ps, Seqdata data)
{
check(ps);
ps->data[ps->size] = data;
ps->size++;
}
顺序表的尾删
顺序表的尾删,我们需要注意的是我们需要多判断一下顺序表中是否为有数据,如果没有就无法进行删除,当size-1是正好为数组最后的下标,将其赋值为0即可,最后将size--完成操作。
void Poptail(SL* ps)
{
assert(ps);
assert(ps->size > 0);
ps->data[ps->size - 1] = 0;
ps->size--;
}
顺序表的头增
顺序表实现头增,我们需要判断两种情况,一种为顺序表没有数据,这时候就可以直接将数组第一个元素直接赋值,如果是有元素的情况下,就需要将数据进行搬运从而将数据放入头部。
我们如果搬数据我们有两种方式,一种从前向后遍历,另外一种从后向前遍历。
我们可以试想一下如果从前向后遍历的化,第二个存放第一个的数据,这时第二个的数据已经被修改了,再往后数据就一直为第一个的数据,所以从前向后无法实现头插。
所以使用头插我们需要使用另外一种遍历方式,从后向前。
void Pushhead(SL* ps, Seqdata data)
{
check(ps);
if (ps->size == 0)
{
ps->data[0] = data;
}
else
{
int i = 0;
for (i = ps->size; i >0 ; i--)
{
ps->data[i] = ps->data[i - 1];
}
ps->data[0] = data;
}
ps->size++;
}
顺序表的头删
顺序表的头删既然是删除所以一定要判断顺序表是否为空,顺序表中只有一个数据时,直接将其赋值为0,如果有多个就和头插类似需要从前向后遍历或从后向前遍历。
如果时从后向前,如果是这样的写法就会是数据一直被最后数据覆盖,所以很明显是不行的。
如果是从前向后的化,就可以成功实现。
void Pophead(SL* ps)
{
assert(ps);
assert(ps->size>0);
if (ps->size == 1)
{
ps->data[0] = 0;
}
else
{
int i = 0;
for (i = 0; i < ps->size-1; i++)
{
ps->data[i] = ps->data[i + 1];
}
}
ps->size--;
}
顺序表的销毁
由于我们开辟了空间,那么我们就一定需要进行释放,将空间还给操作系统。
void DestroySeq(SL* ps)
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->capicity = 0;
ps->size = 0;
}
顺序表的查找
我们如果要查找顺序表的数据的话,可以通过size遍历整个数组,如果找到就返回下标,如果没有找到就返回-1。
int Finddata(SL* ps, Seqdata data)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->data[i] == data)
{
return i;
}
}
return -1;
}
顺序表的下标插入
执行插入操作我们需要输入插入的下标,插入的下标我们可以断言一下,插入的下标不能为负数也不能超过最后一个数据的下标,分情况讨论,如果没有数据的话且输入下标为0就直接给第一个元素赋值,其他的我们可以模仿头插的方式来搬运数据,结束条件为大于传来的下标。
void SLInsert(SL* ps, int pos, Seqdata data)
{
assert(pos >= 0&&pos<=ps->size);
check(ps);
if (ps->size == 0)
{
ps->data[0] = data;
}
else
{
int i = 0;
for (i = ps->size; i > pos; i++)
{
ps->data[i] = ps->data[i - 1];
}
ps->data[pos] = data;
}
ps->size++;
}
顺序表的下标删除
顺序表要进行下标删除时,因为是删除,所以一定要判断是否有数据,分情况讨论,如果只有一个直接将其赋值为0即可,如果要删掉的下标为最后一个的话,可以直接将最后一个数组元素置为0,剩下的进行删除时模仿头删的方式来搬运数据,从而实现删除。
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
int end = ps->size - 1;
int i = 0;
if (ps->size == 1)
{
ps->data[0] = 0;
}
else if (end == pos)
{
ps->data[end] = 0;
}
else
{
for (i = pos; i < end; i++)
{
ps->data[i] = ps->data[i + 1];
}
}
ps->size--;
}
顺序表的打印
顺序表打印我们可以使用遍历的方式使其依次打印,同样也要判断顺序表是否为空。
void SLpint(SL* ps)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->data[i]);
}
}
结语
顺序表在编写时要注意申请的空间及时释放,并且要注意传入的数据是否可行,如果不行会造成越界等问题记得在开始就将其断言,希望我的这篇文章对你有所帮助。
标签:ps,顺序,下标,实现,int,data,size From: https://blog.csdn.net/m0_73736626/article/details/140385889