顺序表是最简单的数据结构之一,我们从其进入数据结构的世界。
顺序表分为动态顺序表与静态顺序表,其逻辑结构和物理结构(存储结构)上都是连续的
静态顺序表即为普通数组;动态顺序表即用动态开辟的内存作为数组存储空间的数组
本质顺序表其实就是类似于数组,所以我们要模拟实现的即是一个“自己的”数组
本篇模拟实现的类似于STL中的deque容器,也就是双端队列,由于静态数组的局限性明显,故此实现的为动态顺序表。即我们要自行实现动态顺序表的增删查改等基本功能。
我们的顺序表结构如下:
typedef int SQDataType; typedef struct Seqlist { SQDataType *a; int size; int capacity; }SeqList;
其中包括了我们存储数据的“动态”数组,以及该数组的数据个数和容量大小。
“动态”即表现在当数组的数据个数等于容量时,我们的存储数据的指针所指向的空间可以通过动态内存函数realloc函数重新申请空间来实现扩容。
扩容过程如下:
static void CheckCapacity(SeqList *plist) { if(plist->size == plist->capacity) { SQDataType *tmp = (SQDataType*)realloc(plist->a,sizeof(SQDataType*)*plist->capacity*2); assert(tmp); plist->a = tmp; plist->capacity *= 2; } }
对容器进行初始化:所有容器使用前都要进行初始化
就如同我们声明变量并初始化一样,能够让其更好的进行工作
第一个函数:InitList()
void InitList(SeqList *plist) { plist->a = (SQDataType*)malloc(sizeof(SQDataType)*4); plist->size = 0; plist->capacity = 4; }
我们在初始化时,将容器的存储指针进行了初始化,以及容器的数据个数,容量大小等
初始化完毕后即可进行增删改查了
首先是进行尾插数据:push_back()
void PushBack(SeqList *plist,SQDataType x) { assert(plist); CheckCapacity(plist); plist->a[plist->size] = x; plist->size++; }
还有头插数据:push_frount()
void PushFront(SeqList *plist,SQDataType x) { assert(plist); CheckCapacity(plist); int end = plist->size - 1; while(end >= 0) { plist->a[end+1] = plist->a[end]; end--; } plist->a[0] = x; plist->size++; }
顺序表的头插数据与尾插数据并没有很复杂的地方,因为是插入数据,所以在插入前都要对容器的大小进行判断,检测是否需要扩容。
tips:头插的数据时我们可以看到其效率是比较低的,需要将头部以后的数据全部都要往后挪动才可从头部进行数据的插入。
紧接着呢就是头删数据与尾删数据了
头删数据:pop_front()
void PopFront(SeqList *plist) { assert(plist->size > 0); int begin = 0; while(begin < plist->size) { plist->a[begin] = plist->a[begin+1]; begin++; } plist->size--; printf("头删成功!\n"); }
头删即采用的是往前覆盖要删除的数据的方式进行,效率也相对较低
尾删数据:pop_back()
void PopBack(SeqList *plist) { assert(plist->size > 0); plist->size--; printf("尾删成功!\n"); }
尾删就很容易了,只要将size(容器数据个数)直接减一即可,那后续通过容器的API就无法访问到该数据了,并且添加时也会自动将其覆盖。(可自行想象数组的大小减少了1,那最后一个数据就自然访问不到了)
最后就是查找以及修改了,其实它们俩差不多,因为修改的前提就是要查找嘛
查找:find()
int Find(const SeqList *plist,SQDataType x) { assert(plist); if(plist->size == 0) { printf("当前顺序表为空!\n"); return 0; } int i; for(i = 0; i < plist->size; i++) { if(plist->a[i] == x) return i; } printf("未找到该数据!\n"); }
这里由于是保存的为整型故返回值为整型,如果保存的是其它类型,对应更改返回类型即可
修改:modify()
void ListModify(SeqList *plist,int pos,SQDataType x) { assert(plist); if(plist->size == 0) { printf("当前顺序表里无数据!\n"); return ; } plist->a[pos] = x; }
修改时传入对应的位置,在该位置进行相应的修改即可
最后呢,要补充的即是随机插入与随机删除了
随机插入:insert()
void Inerst(SeqList *plist,int pos,SQDataType x) { assert(plist); CheckCapacity(plist); int end = plist->size - 1; while(end >= pos) { plist->a[end + 1] = plist->a[end]; end--; } plist->a[pos] = x; plist->size++; printf("插入成功!\n"); }
找到要插入的位置,将其后面的元素都往后挪动,然后插入数据即可
随机删除:erase()
void Erase(SeqList *plist,int pos) { assert(plist->size > 0); int str = pos; while(str < plist->size) { plist->a[str] = plist->a[str + 1]; str++; } plist->size--; }
找到要删除的位置,将其后面所有元素依次往前挪动覆盖即可
至于销毁顺序表以及打印顺序表内的数据,就是很基础的啦
销毁顺序表:destroy()
void Destroy(SeqList *plist) { free(plist->a); plist->a = NULL; plist->capacity = 0; plist->size = 0; }
free掉动态开辟的空间(防止内存泄漏),并将指针置空,其余项也初始化即可
打印顺序表:print()
void PrintSList(const SeqList *plist) { int i; for(i = 0; i < plist->size; i++) printf("%d ",plist->a[i]); printf("\n"); }
依次遍历输出即可
至此,整个顺序表的模拟就完毕了,如果对大家有帮助,可以随手点个赞+收藏哩~