1.基本概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。本篇文章主要是介绍一些基本的接口函数实现一些基本功能。
首先建立一个新工程,然后建立头文件Seqlist.h(函数声明)源文件Seqlist.c(实现接口函数[函数定义])和Test.c(测试函数功能)
2.顺序表的一些接口函数
//首先定义一个结构体描述顺序表
typedef int SLDataType;
struct Seqlist
{
SLDataType* a;//定义一个结构体指针指向顺序表开始位置
int size;//数组中存储的实际数据个数
int capacity;//数组实际容量大小
}SL;
【1】SeqlistInit( )初始化
SeqlistInit(SL* ps)
{
ps->a=NULL;
ps->size=0;
ps->capacity=0;
}
【2】SeqlistCheckCapacity()检查容量/扩容
扩容函数void *realloc( void *memblock, size_t size );
用法解读:和void *malloc( size_t size );函数有所不同,malloc函数是直接按照内存大小分配内存空间再返回新的内存空间的地址。而realloc函数则是将原有指针对于的内存空间变为对应内存空间大小,如果原有地址后面还有足够多的连续内存空间则直接返回原有地址,如果没有则在新的地址分配对应内存空间大小,然后再将原有数据拷贝(系统会自动释放原有数据),然后再返回新的地址。
void SeqlistCheckCapacity(SL* ps)//为传址调用可以通过函数改变SL s1里的值
{
if (ps->size == ps->capacity)
{
int newcapacity = (ps->capacity) == 0 ? 4 : (2 * ps->capacity);
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * 2);
if (tmp == NULL)
{
printf("fail to raise capacity");
exit(-1);
}
ps->capacity = newcapacity;
ps->a = tmp;//根据上面解读可能tmp是返回的新地址因此需要重新赋值给a
}
}
【3】SeqlistPushBack()尾插
void SeqlistPushBack(SL* ps, SLDataType x)//传指针SL* ps为传址调用可以通过函数改变SL s1
{
//先要检查容量大小是否合适,如果容量不够则需要扩容
SeqlistCheckCapacity(ps);
ps->a[ps->size] = x;//ps->size对应下一个元素下标
ps->size++;//插入一个新的元素后size变为size+1
}
【4】SeqlistPushFront()头插
void SeqlistPushFront(SL* ps, SLDataType x)
{
//也要先检查容量是否充足
SeqlistCheckCapacity(ps);
//因为顺序表中数据是连续存储的,因此要实现头插就得将数据向后挪动
int end = ps->size;
for (; end > 0; end--)
{
ps->a[end] = ps->a[end - 1];
}
ps->a[0] = x;
ps->size++;
}
【5】SeqlistPopBack()尾删
void SeqlistPopBack(SL* ps)
{
//温柔的方式:if条件语句处理
//如果顺序表为空则不需要做任何处理
if (ps->size > 0)//如果不为空
{
ps->size--;//顺序表中元素实际个数-1
}
//粗暴的方式:进行断言处理assert函数
//如果函数内条件为真则执行,为假则终止程序返回错误原因
//得引用头文件#include<assert.h>
//assert(ps->size > 0);
//ps->size--;
}
最后断言条件为假结果返回详情:
【6】SeqlistPopFront()头删
void SeqlistPopFront(SL* ps)
{
assert(ps->size > 0);
int start = 0;
while (start < ps->size - 1)
{
ps->a[start] = ps->a[start + 1];
start++;
}
ps->size--;
}
【7】SeqlistInsert()任意位置插入
void SeqlistInsert(SL* ps, SLDataType x, SLDataType y)//在下标为x的位置插入y
{
//1.判断元素插入位置是否合理:断言处理
assert(x >= 0 && x <= ps->size);
//2.检查容量是否充足
SeqlistCheckCapacity(ps);
int end = ps->size;
while (end > x)//3.将x及后面元素后移
{
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[x] = y;//4.下标为x位置放入元素y
ps->size++;//5.size变为size+1
}
【8】SeqlistErase()任意位置删除
void SeqlistErase(SL* ps, SLDataType x)
{
//1.先断言处理判断删除位置是否合理
//要想有交集ps->size一定大于0
assert(x >= 0 && x < ps->size);
int start = x;
//2.将下标x之后的元素前挪覆盖下标x对于的元素
while (start < ps->size - 1)
{
ps->a[start] = ps->a[start + 1];
start++;
}
//3.将size变为size-1
ps->size--;
}
【9】SeqlistPrint()打印顺序表
void SeqlistPrint(SL* ps)
{
//1.定义初始位置下标
int start = 0;
//2.通过下标的变换实现顺序表的打印
for (start = 0; start < ps->size; start++)
{
printf("%d ", ps->a[start]);
}
}
【10】SeqlistDestroy()销毁
销毁: 销毁顾名思义就是之前在堆上申请了内存空间用以存储顺序表,现在这个顺序表结束了它的使命,就要将申请的空间进行释放,否则久而久之,申请的内存空间过多而没有销毁会出现所谓的内存泄漏
free()函数用法解读:
void free( void *memblock );头文件为#include<stdlib.h>/#include<malloc.h>
1.free函数会把之前malloc函数或是realloc函数所申请的内存空间释放。
2.但是free函数只是将参数指针指向的内存归还给操作系统,并不会把参数指针置NULL,为了以后访问到被操作系统重新分配后的错误数据,所以在调用free之后,通常需要手动将指针置NULL。
void SeqlistDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;//防止野指针
ps->size = ps->capacity = 0;//初始化size和capacity
}
【11】int SeqlistFind()查找
可以将SL s1中所对应的a指针所指向的内存看做一个数组,然后用数组的查找方法查找元素(顺序查找或是二分查找【二分查找前提是数组元素是有序的】),最后返回对应元素的下标即可。
int SeqlistFind(SL* ps, SLDataType x)
{
//先判断顺序表中元素是否为空
assert(ps->size > 0);
int start = 0;
for (start = 0; start < ps->size; start++)
{
if (x == ps->a[start])
{
return start;
}
}
return -1;
}
【12】SeqlistModify()修改
void SeqlistModify(SL* ps, SLDataType pos, SLDataType x)
{
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
3.一些改进
在实现了任意位置插入及删除函数后便可以进行以下改进:
【1】头插/尾插
//尾插
void SeqlistPushBack(SL* ps, SLDataType x)
{
SeqlistInsert(ps, ps->size, x);
}
//头插
void SeqlistPushFront(SL* ps, SLDataType x)
{
SeqlistInsert(ps, 0 , x);
}
【2】头删/尾删
//尾删
void SeqlistPopBack(SL* ps)
{
SeqlistErase(ps,ps->size);
}
//头删
void SeqlistPopFront(SL* ps)
{
SeqplistErase(ps,0);
}
总结:
其实只要我们了解了顺序表的基本结构,实现这些接口函数就比较容易了,可以通过画图的方式理清思路,然后再进行代码实现,这也是我们在学习数据结构的过程中很重要的一个习惯。同时也要一步步调试发现错误,不要到最后写完了发现一堆错误之后再找错误。
喜欢的话记得点赞+收藏哦!!!