前言
大家好呀,一瞬间c语言已经快学完啦,现在呢我将跟着大家一起学习初级数据结构---顺序表的实现,我们一起来看看吧。(源码放在最后)
顺序表的概念
顺序表基于数组的一种特殊的链表,表现在和数组一样具有连续的物理结构和逻辑结构(顺带一提链表不一定具有连续的物理结构但是一定具有连续的逻辑结构),这里我们实现的是动态顺序表,与数组与静态顺序表相比,有可扩容,不浪费空间的特性,在数据的增删查找方面也更具优势,我们现在即将完成就是动态顺序表的增删查改的代码。
前期准备
我们准备在vs2022里面创建一个顺序表头文件(用于声明顺序表源文件的代码充当目录的作用,另外本文所有方法均有在头文件中声明,就不单独贴图了)一个顺序表源文件以及测试文件如下,个人练习命名就随自己喜欢啦。
然后在写顺序时需要用到结构体知识,所有操作放在图里啦(个人认为很详细了!)操作包括包含头文件,定义了需要的结构体,如下
前期准备就到这里啦,是不是很简单
顺序表的初始化
接下来实现顺序表的初始化操作,给上一个slin函数实现初始化操作
这里我们默认把arr置为NULL,初始也可以给arr一个空间,这时需要把capicity和arr空间大小对应,传地址的问题时因为不这么做的话属于传值,但是我们在test文件(下面有图)里不会把sl初始化编译器会报错。快速在test文件里给上一个main函数测试。
扩容函数的书写
可扩容是动态顺序表的特征和优势所在,要想对顺序表进行后续操作也需用到扩容,思路是如果SL结构体的有效数据size和数组长度capicity相等时,表示数组无可用空间,这时我们需要用realloc函数开辟一个2倍数组长度的空间,代码如下
数据的前插与后插
在完成扩容函数后,我们后插就只需要在检查空间够不够后在arr[size]位置放上所需数据后size++就可以啦(这里合成了一句代码)(x表示要插入的数据)
前插我们需要把数组所有元素后移一位然后在arr[0]处放上插入的元素(x是要插入的数据)
好像也没有太多需要解释的地方,大家多看看吧哈哈
数据的前删与后删
后删只需要一行代码size--即可,不会影响我们后续对于顺序表的增删查改,前删要把除了第一个元素以外的元素前移后再size--,这样第一个元素被覆盖实现前删的效果
数据的指定位置插入
指定位置插入需要函数多一个位置参数,把此位置参数后的所有元素都后移一位,然后在此位置处插入指定数据即可,当然,还需断言指定的位置合法才能插入,判断是否需要扩容的操作也必不可少
别忘了size++哦。
数据指定位置删除
和指定位置的查找类似,需要我们把指定位置的数据删除后在把后面的元素全部往前移一位即可,难点在确定for循环终止时刻指针的位置最好画图理解
别忘了size--哦。
数据查找
当指定数据等于数组中的数据时,我们返回数组的下标,当数组中不存在这个数据时返回一个小于0的数表示该数不存在。(即没有数组对应下标)
最后在主函数里给上一个判断函数存在与否的if-else语句查找就完成啦
数组打印
for循环遍历数组打印就完成啦
只需要注意在传参时传的并不是SL的地址即可,因为打印是不需要我们对数组内容进行修改的
尾声
顺序表的基本操作就这些啦,现在你也可以自己动手写一个顺序表啦
sl.c
#include"sl.h"
void slin(SL* s)//注意这里需要传地址
{
s->arr = NULL;
s->size = s->capicity = 0;
}
void sldestory(SL* s)
{
if (s->arr)
free(s->arr);
s->arr = NULL;
s->size = s->size = 0;
}
void static checksl(SL* s)
{
assert(s);//传过来的数组不能为NULL
if (s->capicity == s->size)
{
int newcapicity = s->capicity == 0 ? 4 : 2 * s->capicity;/*三目操作符,表示如果问句
成立开辟一个4个整形空间,不成立capicity乘上二*/
sldatatype* tmp/*为了防止开辟空间失败返回NULL创建的临时指针*/ =
(sldatatype*)realloc(s->arr, newcapicity * sizeof(sldatatype));
if (tmp == NULL)
{
perror("realloc fail");
exit(1);//直接退出程序,不再继续执行
}
//空间申请成功
s->arr = tmp;
s->capicity = newcapicity;
}
}
void slpushback(SL* s, sldatatype x)
{
checksl(s);
s->arr[s->size++] = x;
}
void slpushfront(SL* s, sldatatype x)
{
checksl(s);//仍然先检查内存空间够不够,不够需要开辟空间
for (int a=s->size; a >0 ; a--)
{
s->arr[i] = s->arr[i-1];
}//后移操作
s->arr[0] = x;
s->size++;//别忘记size++
}
void print(SL s)
{
for (int y = 0; y < s.size; y++)
{
printf("%d ",s.arr[y]);
}
printf("\n");
}
void slpopback(SL* s)
{
assert(s);
assert(s->size);
s->size--;
}
void slpopfront(SL* s)
{
assert(s);
assert(s->size);
for (int c = 0; c < s->size-1; c++)
{
s->arr[c] = s->arr[c + 1];
}//数据整体前移
s->size--;
}
void slpush(SL* s, int pos, sldatatype x)
{
assert(s);
assert(0<=pos<=s->size);
checksl(s);
for (int a =s->size ; a >pos ; a--)
{
s->arr[a] = s->arr[a-1];//arr[pos+1]=arr[pos]
}
s->arr[pos] = x;
s->size++;
}
void slerase(SL* s, int pos)
{
assert(s);
assert(0 <= pos < s->size);//右边小于不能取等
for (int i = pos; i < s->size-1;i++)
{
s->arr[i] = s->arr[i + 1];//最后时s->arr[size-2]=s->arr[size-1]
}
s->size--;
}
int slfind(SL* s, sldatatype x)
{
assert(s);
for (int i = 0; i < s->size; i++)
{
if (s->arr[i] == x)
{
return i;
}
}
return 0;
}
sl.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//需要用到的头文件包含在我们自定的头文件中即可
typedef int sldatatype;//这一步为了便于以后实现非整形顺序表时,把需要改变的数据类型实现一键替换
typedef struct seqlist
{
sldatatype* arr;//不定长的数组
int size;//计算数组中有效数据个数
int capicity;//计算数组大小
}SL;//为了方便我们后续使用,这里是相当于为数组重新取了个名字
void slin(SL* s);
void sldestory(SL* s);
void slpushback(SL* s, sldatatype x);
void slpushfront(SL* s, sldatatype x);
void slpopback(SL* s);
void slpopfront(SL* s);
void print(SL s);
void slpush(SL* s, int pos, sldatatype x);
void slerase(SL* s, int pos);
int slfind(SL* s, sldatatype x);
test.c
#include"sl.h"
void slintest()
{
SL s1;
slin(&s1);
slpushback(&s1, 1);
slpushback(&s1, 2);
slpushback(&s1, 3);
slpushback(&s1, 4);
print(s1);
int ret = slfind(&s1, 3);
if (ret < 0)
printf("找不到你要找的数字\n");
else
printf("找到了,下标时是%d\n",ret);
}
int main()
{
slintest();
return 0;
}
结束啦,下次再见。