1.概念及结构
1. 静态顺序表:使用定长数组存储元素
#define N 7
2. 动态顺序表:使用动态开辟的数组存储。
typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);
2.五大功能实现
2.1尾插
void SeqListPushBack(SL* ps1, SLDataType x)
assert(ps1);
CheckCapacity(ps1);
ps1->a[ps1->size] = x;
ps1->size++;
我们先进行断言 后检查一遍容量 都可行后 尾插可以直接放入之后的空间
直接将x放入下标为size即可
2.2头插
void SeqListPushFront(SL* ps1, SLDataType x)
void SeqListPushFront(SL* ps1, SLDataType x)
{
assert(ps1);
CheckCapacity(ps1);
//挪动数据
int end = ps1->size - 1;
while (end >= 0)
{
ps1->a[end + 1] = ps1->a[end];
end--;
}
ps1->a[0] = x;
ps1->size++;
}
头部插入我们先进行断言和检查容量
逐步进行挪动 最后赋值到首元素地址并进行 size++
2.3尾删
void SLPopBack(SL* ps1)
void SLPopBack(SL* ps1)
{
assert(ps1);
assert(ps1->size > 0);
ps1->size--;
}
思想:尾删我们直接将size-- 这样他就没有访问原size的权限 哪怕原size的数据还存在 那他也访问不到了
2.4头删
void SLPopFront(SL* ps1)
void SLPopFront(SL* ps1)
{
int begin = 1;
while (begin < ps1->size)
{
ps1->a[begin - 1] = ps1->a[begin];
++begin;
}
ps1 ->size--;
}
头删我们采用覆盖的方法 就是将下一个元素赋值给上一个元素 所以我们定义一个下标从1开始覆盖第一个元素
2.5指哪删哪
void SLErase(SL* ps1, int pos) pos是下标
void SLErase(SL* ps1, int pos)
{
assert(ps1);
assert(pos >= 0 && pos < ps1->size);
int begin = pos + 1;
while (begin < ps1->size)
{
ps1->a[begin - 1] = ps1->a[begin];
++begin;
}
ps1->size--;
}
首先我们要判断pos的判断条件 找到对应下标进行+1存为begin下标 判断循环
2.6指哪加哪
void SLInsert(SL* ps1, int pos, SLDataType x)
void SLInsert(SL* ps1, int pos, SLDataType x)
{
assert(ps1);
assert(pos >= 0 && pos <= ps1->size);
CheckCapacity(ps1);
//挪动数据
int end = ps1->size - 1;
while (end >= pos)
{
ps1->a[end + 1] = ps1->a[end];
--end;
}
ps1->a[pos] = x;
ps1->size++;
}
删不需要检查容量 而我们增加i据需要检查容量了
原理也是挪动数据 我们原理是把x赋值给下标为pos的值 然后其他依此向后挪动
3. 接口实现
3.1自定义类型
我们首先进行头文件说明
typedef int SLDataType
这是为了我们后续在创建变量涉及到整形 浮点型转换时不必过于麻烦
typedef struct SeqList
{
SLDateType* a;
int size;
int capacity;
}SL;
在这里我们创建了自定义结构体类型 定义了一个a指针 size表示数组长度 capacity表示数据容量
随后我们进行正式的函数实现
3.2顺序表的初始化
void SLInit(SL* ps1);
我们在SeqList.h文件里声明函数
创建一个源文件来实现函数声明
void SLInit(SL* ps1)
{
assert(ps1);
ps1->a = NULL;
ps1->size = 0;
ps1->capacity = 0;
}
在这里我们创建结构体指针
1:进行assert断言 ps1指针必须不为NULL,
2:对ps1->a进行赋空指针
3.3 顺序表的销毁
void SLDestroy(SL* ps1)
{
assert(ps1);
if (ps1->a != NULL)
{
free(ps1->a);
ps1->a = NULL;
ps1->size = 0;
ps1->capacity = 0;
}
}
逻辑:
1.先进行断言
2.判断数据是否为空 因为如果容量不够 我们在内存中选择动态内容进行开辟
开辟后需要释放空间 否则会内存泄露 所以我们先free 释放后要将a置空 其他赋值0
定义完顺序表的基本框架后 我们下面开始实现如何基本功能
3.4打印函数
oid SLPrint(SL* ps1)
{
assert(ps1);
for (int i = 0; i < ps1->size; i++)
{
printf("%d", ps1->a[i]);
}
}
这里没什么好说的 直接上代码
3.5检查容量
void SLCheckCapacity(SL* ps1)
{
assert(ps1);
if (ps1->size == ps1->capacity)
{
int newcapacity = ps1->capacity == 0 ? 4 : ps1->capacity * 2;
SLDateType* tmp = (SLDateType*)realloc(ps1->a, sizeof(SLDateType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail : ");
return;
}
ps1->a = tmp;
ps1->capacity = newcapacity;
}
}
首先我们先进行断言
其次,我们为什么要检查容量 目的是什么?
1. 我们如果不适用静态数组 如果我们可容纳10人的信息 而我们储存了11个信息 此时就会栈溢出
所以我们的目的就是为了动态自动扩容
2.我们已经提到了目的是为了防止容量不够才进扩容 所以我们第一步是判断size 是否等于我们的capacity 如果相等 说明我们该扩容了
3.精髓的一步: 将 capacity 赋值给 newcapacity 我们选择使用三目操作符
要考虑到如果容量是0呢? 所以当他为0时我们让他变为4 不为0我们让他进行*2操作
接下来我们要将原来的capacity动态开辟 我们使用realloc 我们创建一个指针来存放数据 以防止如果我们开辟内存失败 原指针也变为NULL
随后赋值给原指针