前言:
本文指在总结C语言顺序表实现的底层原理及表层操作实现,对初学C语言顺序表的人能有个深入的了解,希望能对你有帮助。
目录
一、顺序表与数据结构及线性表
(一)数据结构
顺序表顾名思义,你可以把它看作是一个表格。顺序表是数据结构的一种,因此可以说是刚开始入门数据结构,那么想了解顺序表就必须对数据结构的定义有个认知了解。那么数据结构是什么呢?在生活中你可以有这样的烦恼,在书房里,一本本书散落在房间的各个角落,零零散散,想找一本书需要花费较多的时间。为了提升我们找书的效率我们会将书归纳分类整理到一个书架上,这样在找书的时候只需知道自己所找的书在哪一类就行了。因此可以说书架就是一个数据结构。
用专业点的说法:数据结构就是计算机存储和组织数据的方式。从名字就能看出数据结构有两部份组成,一是数据,其次是结构,将数据组织和存储合适的结构上叫数据结构。
(二)线性表
本文提到的顺序表其实是线性表的一种,线性表是指具有相同特性的数据结构的集合。
两大特性:内存中存储的物理结构不一定连续;逻辑结构一定连续。
线性表有很多种,包括:顺序表、链表、栈、队列、字符串……
(三)顺序表
1.底层原理
顺序表的底层原理其实就是一个数组,只不过顺序表在数组的基础上增加了许多现成的方法,开箱即用,能直接对数组进行增删查改等操作。数组可分为定长数组(初始化的时候给定数组一个初始值大小)和动态柔性数组(数组的大小可改变)。那么基于数组的顺序表也可分为静态顺序表和动态顺序表。
2.分类
顺序表:动态顺序表、静态顺序表。
3.优缺点
静态顺序表:在知道具体的数据数量上,使用静态顺序表,比如有十个字符数据,就给十个字符数组的空间大小。
动态顺序表:在不清楚数据具体容量且数据量动态变化的就使用动态顺序表,可随数据量的大小而变化。
对比:在数据量不明的时候,相较静态顺序表,动态顺序表则显得更具优势,动态顺序表可随数据量的增加而扩充空间,但是静态顺序表如果空间一开始给大了可能造成浪费,给小了可能又会不够用。
二、顺序表操作实现
定义
静态顺序表的定义:
struct Sequlist
{
int arr[100];
int size;
};
//底层是一个定长的数组
动态顺序表定义:
struct Seqlist
{
int* arr;
int size; //有效存储数据个数
int capacity; //空间容量大小
};
//给一个指针,后面动态开辟空间进行扩容
想到顺序表,我们习惯推荐使用动态顺序表,因为相比之下,更有好处。
空间开辟
需要使用到动态空间开辟函数(malloc、calloc、realloc),为了动态扩容,这里使用realloc函数。
struct Seqlist ps; //创建一个顺序表
void Secheckcapacity()
{
if(ps.szie==ps.capacity)
{ //数据容量两倍两倍地扩容(符合数学定律)
int newcapacity = ps.capacity==0 ? 4 : 2 * ps.capacity;
(int *)tem = (int *)realloc(ps.arr,newcapacity * sizeof(int));
if(tem==NULL)
{
perror("realloc:");
exit(1);
}
//扩容成功
PS.arr = tem;
ps.capacity = newcapacity;
}
}
根据数据容量动态开辟空间是动态顺序表的核心要点,了解了这里之后,后面的一系列基于顺序表的操作就很容易实现。
其中方法包括:
顺序表初始化和销毁:
增加插入数据:尾差、头插、指定插入
删除数据:尾删、头删、指定删除
查找数据和修改数据:
具体实现:
初始化:
void SLinit(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
销毁:
void SLdestory(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
}
ps->size = ps->capacity = 0;
}
头插:
void SLpushfront(SL* ps, SLdatatype x)
{
assert(ps);
SLcheckspace(ps);
//插入数据
for (int i = ps->size-1; i >=0; i--)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[0] = x;
ps->size++;
}
尾差:
void SLpushback(SL* ps,SLdatatype x)
{
assert(ps);
//插入前先检查是否有足够空间
if (ps->size == ps->capacity)
{ //会报错,capacity初始值为0,(可以在顺序表初始化时给其空间,但同时要开辟相应的空间)
//ps->arr = (SLdatatype*)realloc(ps->arr, ps->capacity * 2 * sizeof(SLdatatype));
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLdatatype* tem = realloc(ps->arr, newcapacity * sizeof(SLdatatype));
if (tem == NULL)
{
perror("perror:");
exit(1);
}
//空间开辟成功
ps->arr = tem;
ps->capacity = newcapacity;
}
//插入数据
ps->arr[ps->size] = x;
ps->size++;
}
指定插入:
void SLinsect(SL* ps, int pos,SLdatatype x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLcheckspace(ps);
for (int i = ps->size - 1; i >=pos ; i--)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[pos] = x;
ps->size++;
}
尾删;
void SLdelback(SL* ps)
{
assert(ps);
assert(ps->size);//确保有数据可删
ps->size--; //不影响其他方法,可以直接将size--;
//另一种方法(可行,但没必要)
//ps->arr[ps->size - 1] = -1;
//ps->size-
}
头删:
void SLdelfront(SL* ps)
{
assert(ps);
assert(ps -> size);
for (int i = 0; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
指定删:
void SLdelete(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
查找数据:
int SLcheck(SL sl, SLdatatype x)
{
for (int i = 0; i < sl.size; i++)
{
if (sl.arr[i] == x)
{
printf("找到了!\n");
return i;
}
}
printf("没找到\n");
return -1;
}
修改数据:
void SLmodify(SL* ps, int pos, SLdatatype x)
{
assert(ps);
ps->arr[pos] = x;
}