顺序表本质就是个数组,存放在里面的数据必须是连续的。
话不多说,直接上代码 ,代码注释算是比较详细了。
SeqList.h(顺序表在这里创建 ,声明一下函数。)
#pragma once //防止被重复包含
#include <stdio.h>
#include<assert.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<windows.h>
typedef int SLDataType;//这样做的好处可以随时更改顺序表的数据类型
//这里的SLDataType就是类型名 现在它就是int
//vector
typedef struct SeqList
{
SLDataType* a;//顺序表的表长 指向动态开辟的数组
int size;//有效数据的个数 同时也是最后一个元素的下标
int capacity;//容量
}SL,SeqList;
void SeqListInit(SL* ps);
void SeqListDestory(SL* ps);
void SeqListPrint(SL* ps);//打印插入的值
void SeqListCheckCapacity(SL *ps);//检查容量
void SeqListPushBack(SL* ps, SLDataType x);//尾部插入
void SeqListPopBack(SL* ps);//尾部删除
void SeqListPushFront(SL* ps, SLDataType x);//头上的插入
void SeqListPopFront(SL* ps);//头上的删除
//中间位置插入和删除
void SeqListInsert(SL* ps, int pos, SLDataType x);//结构体指针 插入的位置 插入的数据
void SeqListErase(SL* ps, int pos);//结构体指针 删除的位置
//顺序表查找
int SeqListFind(SL* ps, SLDataType x);
SeqList.c(声明函数的实现)
//SeqList的实现
void SeqListInit(SL* ps)
{
/*s.size = 0;
s.a = NULL;
s.capacity = 0;*/
//初始化顺序表 为其开辟空间
ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//开辟了四个SLDataType大小的空间
if (ps->a == NULL)//结构体指针 访问结构体成员用->
{
printf("申请内存失败\n");
exit(-1);//直接结束掉程序
}
ps->size = 0;
ps->capacity = 4;
}
//打印顺序表内容
void SeqListPrint(SL* ps)
{
int i = 0;
assert(ps);
for ( i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//检查空间是否足够
void SeqListCheckCapacity(SL* ps)
{
//如果满了需要增容 一般增加到原来的二倍 增的少 增加频繁 增的多 浪费空间
if (ps->size >= ps->capacity)//如果数组个数大于等于数组容量 说明数组满了
{
ps->capacity *= 2;
ps->a = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity);//realloc 参数1 原来的空间 参数2 你要的新空间
if (ps->a == NULL)//如果数组为空
{
printf("扩容失败\n");
exit(-1);//直接干掉程序 这句话太粗暴 就像生病了直接抢救都不抢救直接埋了
}
}
//如果后面有足够的空间 原地增容,若后面没有足够的空间 找一块新的空间把数据拷过去,再释放掉旧的空间
}
//销毁顺序表
void SeqListDestory(SL* ps)
{
free(ps->a);//把指针所指向的空间释放
ps->a = NULL;//把指针变为空指针
ps->size = ps->capacity = 0;//再把其他数据都变为0
}
//尾部插入
void SeqListPushBack(SL* ps, SLDataType x)
{
/*
assert(ps);//ps一定不能为空 为空直接让它报错
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;//通过结构体指针访问数组a, ps->size这个位置的元素 插入这个位置的值为x
ps->size++;//加一个长度
这里面的代码等同于下面这一行
*/
SeqListInsert(ps, ps->size, x);
}
//尾部删除
void SeqListPopBack(SL* ps)
{
assert(ps);
//ps->a[ps->size - 1] = 0; 你的电脑删除某个软件 是因为只是把文件系统的链接删除了 真实文件没有删 直到这些文件的空间被其他文件覆盖 才会完全消息
//所以上面这行代码不需要 万一那个地方本来就是0呢
ps->size--;
}
//头部插入 //插入的时候一定要注意size是否超过了Capacity
void SeqListPushFront(SL* ps, SLDataType x)//头上的插入 //没有接口可以直接在数据前方增加空间 只能先把要插入位置的数据往后挪
{
/*
assert(ps);
SeqListCheckCapacity(ps);//调用扩容函数
int end = ps->size - 1;//end是这个数组的最后一个元素
while (end >=0 )//一直到第一个元素也被往后挪,循环停止
{
ps->a[end + 1] = ps->a[end];//从前往后挪
--end;//end的位置往前走一位
}
ps->a[0] = x;//把x放进头上的位置
ps->size++;//给有效数据的个数,加一位
这里面的所有代码等同于下面一行
*/
SeqListInsert(ps, 0, x);
}
//头上的删除
void SeqListPopFront(SL* ps)
{
assert(ps);
int start = 0;
while (start<= ps->size-2)//最后一个元素是size-1 而start是把后面的元素往前面挪 所以start的最后一个位置是size-2
{
ps->a[start] = ps->a[start + 1];//从后往前挪 把第一个元素给覆盖掉 再把最后一个元素给删掉
++start;
}
ps->size--;
}
//中间位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x) //结构体 插入的位置 插入的数据
{
assert(ps);
assert(pos <= ps->size && pos >= 0);//这个地方小于等于ps->size 上面的尾插就可以不写了 直接调用
SeqListCheckCapacity(ps);//调用扩容(检查 )函数
int middleInsert = ps->size - 1;//middleInsert是最后一个元素的位置
while (middleInsert >= pos)//如果middleInsert的位置在pos的位置后面
{
ps->a[middleInsert + 1] = ps->a[middleInsert];//将pos要插入的位置后面的元素全部向后挪
--middleInsert;//这里写成++ 写成个死循环了..
}
ps->a[pos] = x;//数据放进去
ps->size++;//元素个数加1
}
//中间位置删除
void SeqListErase(SL* ps, int pos) //结构体 删除的位置
{
assert(ps);
int middleDel = pos;//middleDel是pos的右边
while (middleDel < ps->size -1)//停止结果 小于等于最后一个位置
{
ps->a[middleDel] = ps->a[middleDel+1];//将pos的位置挪到最后 然后删除
++middleDel;
}
ps->size--;
}
//查找
int SeqListFind(SL* ps, SLDataType x) //找到后 返回该元素的下标
{
assert(ps);
int i = 0;
while (i < ps->size)
{
if (ps->a[i] == x)
{
return i;//找到返回i
}
++i;
}
//找不到
return -1;
}
test.c(测试顺序表)
//最好写一个接口测试一个接口 如果一起测,错误了不知道哪个借口有错误!!!
//顺序表
//测试头尾插入删除
void TestSeqList1()
{
//尾部插入测试
SeqList s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);//从这里开始已经发生越界了
SeqListPushBack(&s, 6);//越界就像查酒驾 必须在那个点被逮住了 才会报错 也就是说越界编辑器可能不报错
SeqListPushBack(&s, 7);//最后size为7 capacity为4 你4个空间现在有7个元素
SeqListPushBack(&s, 8);//扩容过了就没事了
SeqListPushBack(&s, 9);
SeqListPrint(&s);
//尾部删除测试
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPrint(&s);
//头部插入测试
SeqListPushFront(&s, -1);//插入-1
SeqListPushFront(&s, -2);
SeqListPushFront(&s, -3);
SeqListPrint(&s);
//头部删除测试
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
//中间插入
SeqListInsert(&s, 4, 5);//在下标为4的地方插入 一个元素5
SeqListPrint(&s);
//中间删除
SeqListErase(&s, 5);//删除下标是五的元素 也就是第六个元素
SeqListPrint(&s);
int pos = SeqListFind(&s, 5);//找到5这个元素的下标 赋给pos
if (pos != -1)
{
SeqListErase(&s, pos);//根据下标删除5这个元素
}
SeqListPrint(&s);
SeqListDestory(&s);
}
int main()
{
TestSeqList1();
}