线性表介绍,顺序表讲解以及实现
线性表
众所周知,线性表是一个n个具有相同特性的数据元素的有限序列,在现实学习中使用非常广泛,例如非常常见的线性表:顺序表,链表,栈,队列等等,虽然它在逻辑上是线性结构,但是在物理结构并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式储存,今天也就是要实现最简单基础的顺序表

顺序表的讲解
顺序表的概念
顺序表,顾名思义,就是用一段物理地址连续的储存单元依次储存数据元素的线性结构,一般情况下采用数组储存,在数组上就可以完成数据的增删查改。
顺序表的种类
顺序表分为静态顺序表和动态顺序表,静态顺序表采用定长数组来储存元素,简单且不实用:动态顺序表采用动态开辟的数组来进行存储数据,若存储的空间不够可以及时扩容,可以较好的完成任务,我们今天实现的也是动态顺序表
顺序表的实现
当然,讲到这里,有的兄弟们可能就要问了,讲了这么多听着恶心的东西,那我们该怎么实现呢,首先我们先看看动态顺序表的接口,然后把它们引入到头文件中,然后我们再一个一个实现出来,今天的任务就完成啦

顺序表的接口实现
typedef int SLDataTpye;
typedef struct SeqList{
SLDataTpye* a;
int size;
int capacity;
}SeqList;
void SeqListInit(SeqList* p);
void SeqListPrint(SeqList* p);
//时间复杂度为O(1)
void SeqListPushBack(SeqList* p, SLDataTpye x);
void SeqListPopBack(SeqList* p);
//时间复杂度为O(N)
void SeqListPopFront(SeqList* p);
void SeqListPushFront(SeqList* p, SLDataTpye x);
void SeqListInsert(SeqList* p, int pos, SLDataTpye x);
void SeqListErase(SeqList* p, int pos);
void checkCapacity(SeqList* p);
void SeqListDestroy(SeqList* p);
好的,我们很明显的看到,动态顺序表采用了SLDataType 了一个指针,这个指针便是需要动态开辟的指针了,任务的进行,这个指针不可或缺,size表示有效数组的个数,capacity表示容量空间的大小,剩余的接口根据名称便可大致知道其功能一二,如果不知道的话建议不要再溜冰了!!!*

接口的具体实现
相信只有真正的家人才能看到这里,那么,现在将介绍真正的代码实现,若有错误,也请各位朋友一一指正
void checkCapacity(SeqList* p) {
assert(p);
if (p->size == p->capacity) {
size_t newcapacity = p->capacity == 0 ? 4 : (p->capacity) * 2;
SLDataTpye* tmp = realloc(p->a, sizeof(SLDataTpye) * newcapacity);
if (tmp) {
p->a = tmp;
p->capacity = newcapacity;
}
else {
printf("alloc fail\n");
exit(-1);
}
}
}
void SeqListInit(SeqList* p) {
p->a = NULL;
p->size = 0;
p->capacity = 0;
}
首先出场的便是检查容量以及初始化顺序表,当p的容量不够时便赶紧扩容到先前的2倍,但是感觉指数级增长的太快了,有点占用内存,但是瑕不掩瑜,这点小伤无足挂齿;初始化顺序表没有甚么可以值得谈一谈的,请大家自行理解
void SeqListPushFront(SeqList* p, SLDataTpye x) {
checkCapacity(p);
/*int tmpsize = p->size;
while (tmpsize > 0) {
p->a[tmpsize] = p->a[tmpsize - 1];
tmpsize--;
}
p->a[0] = x;
p->size++;*/
SeqListInsert(p,0,x);
}
void SeqListPushBack(SeqList* p, SLDataTpye x){
checkCapacity(p);
/*p->a[p->size] = x;
p->size++;*/
SeqListInsert(p, 0, p->size-1);
}
void SeqListPopBack(SeqList* p) {
checkCapacity(p);
if (p->size > 0) {
p->size--;
}
}
void SeqListPopFront(SeqList* p) {
assert(p);
if (p->size != 0) {
int tmpbegin = 0;
while (tmpbegin < p->size - 1) {
p->a[tmpbegin] = p->a[tmpbegin + 1];
tmpbegin++;
}
p->size--;
}
else {
printf("The size is empty\n");
return;
}
}
紧接着便是push和pop的四组,由此可见,从后面进行的操作都非常简单,从前面进行的操作都相对复杂一点,但是还是很好理解的
void SeqListPrint(SeqList* p) {
assert(p);
if (p->size!=0) {
for (int tma = 0; tma < (p->size)-1; tma++) {
printf("%d ",p->a[tma]);
}
printf("%d\n",p->a[p->size-1]);
}
else {
printf("There is no data\n");
return;
}
}
就是一个普通的打印函数,随意看一下理解便好
//pos从0开始算
void SeqListInsert(SeqList* p, int pos, SLDataTpye x) {
checkCapacity(p);
int tmpsize = p->size;
if (pos >= 0 && tmpsize >= pos) {
while (tmpsize > pos) {
p->a[tmpsize] = p->a[tmpsize - 1];
tmpsize--;
}
p->a[pos] = x;
p->size++;
}
else {
printf("The pos is not suit\n");
return;
}
}
void SeqListErase(SeqList* p, size_t pos) {
assert(p);
assert(pos < p->size);
size_t tmpsize = pos + 1;
while (tmpsize < p->size) {
p->a[tmpsize - 1] = p->a[tmpsize];
tmpsize++;
}
p->size--;
}
void SeqListDestroy(SeqList* p) {
assert(p);
free(p->a);
p->a = NULL;
p->capacity = p->size = 0;
}
好的,最后的三组函数出现了! 但是很好理解,第一个insert插入函数也就是从后往前遍历,给要插入的数值腾出地方;第二个erase消除函数很好理解;最后的destroy销毁顺序表的函数,有一个注意点便是free掉p->a后,需要将它变成NULL,防止它成为野指针被使用,那就会出乱子了。
顺序表的优缺点
当然,顺序表虽然简单,但是也是有优缺点的,优点是它使用了连续的物理空间,方便下标随机访问;但是缺点更加明显,1,插入数据时,空间不足要扩容,扩容有会性能消耗;2,头部或者中间位置插入或者删除数据时,需要挪动数据,效率比较低;3,不能按照需求来申请和释放空间,可能会存在一定的空间占用,浪费空间。
顺序表总结
顺序表作为数据结构的基础,对于新手来说还是非常友好的,也希望我能一直坚持下去,努力完成学业,也希望看到这里的观众事业顺利,心想事成,下次再见

本文详细介绍了顺序表的基本概念、静态与动态的区别,重点展示了动态顺序表的接口实现,包括初始化、扩容、操作函数,同时讨论了其优缺点。
5891

被折叠的 条评论
为什么被折叠?



