目录
2.1->定义顺序表结构,每一步都有详细解释哦注意看代码后边的注释!!!
2.3->容量检查函数!!!重中之重,没有这一步你的顺序表就做不出来,就和普通数组没有区别,不能自动扩容
1->认识顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
那么小伙伴们就会疑惑了?你说顺序表是用数组实现的,那顺序表还有存在必要吗?顺序表和普通数组又有什么区别呢?答案:顺序表在查询速度上是OK的有必要存在,他与数组有以下区别:
顺序表的长度可以动态增长,普通数组的长度是固定的,如定义一个int arr[num];在编译器编译阶段就必须确定数组元素个数,并且固定不会在发生改变!!!
2->开始模拟实现顺序表
2.1->定义顺序表结构,每一步都有详细解释哦注意看代码后边的注释!!!
首先我们需要定义一个顺序表结构体,这个结构体中的各个字段含义如下
(1) _arr : 顺序表底层使用数组存储并管理数据,这是数组起始地址
(2) _size : 表示当前这个顺序表中有多少个有效元素,比如里边存储[1,2,3,4,5],那么_size = 5;
(3) _capacity : 表示数组容量,表示当前这个顺序表最多插入多少个数据,一定要注意当
_size == _capacity时就是没有多余空间可以用来插入数据了,要扩容!!!
/这里使用C语言模拟实现顺序表
//本篇博客以int整形数组为例子
typedef struct sequence_list
{
int* _arr; //顺序表底层使用数组存储并管理数据
int _size; //_size用来记录数组中有效元素的个数
int _capacity; //_capacity用来表示数组的容量
}SeqList; //到这里简单顺序表就构建出来了
2.2->初始化顺序表,使用之前肯定要初始化喽
代码如下:注释一定要认真看,每一步都有讲解
//接下来就是去写一系列的顺序表接口给用户使用----其实大体也就是增删查改
//1.首先肯定是顺序表的初始化了
void seqlist_init(SeqList* slptr)
{
assert(slptr); //保证要初始化的顺序表存在
slptr->_arr = NULL; //防止操作野指针造成不可预估的错误;
slptr->_size = 0; //这个没啥疑问吧?刚创建出来的顺序表我们没插入数据有效元素个数当然是1喽
slptr->_capacity = 0; //初始容量给他个0
}
2.3->容量检查函数!!!重中之重,没有这一步你的顺序表就做不出来,就和普通数组没有区别,不能自动扩容
代码如下:注释一定要认真看,每一步都有讲解
(1)因为在插入数据之前要先判断是否有剩余空间给你插入,如果没有就涉及到再次申请更大的空间
(2)所以将判断空间 + 满了申请更大空间两个工作放在一起
//2.先不要急着去做增删查改,不要忘记在每次插入之前要检查一下容量是否够你插入
void seqlist_check_capacity(SeqList* slptr)
{
assert(slptr); //保证要初始化的顺序表存在
if (slptr->_size == slptr->_capacity)//这里容量满了
{
//为了方便小白学习这里每一步我都没跳步,如果还是没看懂评论区留言或者私信我讲给你听!
//为什么判断顺序表当前容量是否为0?别忘了,我们在调用seqlist_init()函数时进行初始化时,做了_capacity= 0的工作,也就是说在你第一次插入数据,顺序表容量为0,所以我们就给它一个初始容量8
int newcapacity = slptr->_capacity == 0 ? 8 : (slptr->_capacity) * 2;
int* newarr = (int*)realloc(slptr->arr,sizeof(int) * newcapacity);
if (newarr == NULL)
{
printf("扩容失败,那我就退出喽!!!\n");
exit(1);
}
//到这里说明扩容成功,就是去进行一些收尾工作就ok了
//一定要记住把旧空间释放,不然就内存泄漏啦
free(slptr->_arr); //就算slptr->_arr == NULL 也不要怕
//在C和C++ 中, free(NULL) 不会出错这是因为标准规定,
//给 free 函数传递空指针是合法操作,函数会直接返回,
//不会进行释放内存等操作,也不会导致程序崩溃或出现错误状态
slptr->_arr = newarr;
slptr->_capacity = newcapacity;
}
}
注意:如果传入realloc的指针为空指针(NULL)那么realloc就和malloc作用一样!!!
2.4->顺序表增删查改操作之尾部插入
顺序表最好的插入就是尾部插入,效率很高
实现要点:插入数据了那你的_size有效元素个数也得增加对吧
//进行尾部插入,尾部删除,头部插入,头部删除
//顾名思义,就是在顺序表头部插入数据,头部删除数据 即 下标 = 0位置插入/删除数据
// 顺序表尾部插入数据,尾部删除数据,即下标 = slptr->_size 的位置插入/删除数据
//尾部插入
void seqlist_push_back(SeqList* slptr, int x)//x是要插入的数据
{
//判断顺序表存在并且检查顺序表的容量
assert(slptr);
SeqList_check_capacity(slptr);
//然后开始插入
slptr->_arr[slptr->_size] = x;
//更新_size有效元素个数
slptr->_size++;
}
2.5->顺序表增删查改操作之尾部删除
顺序表有一个很大的优点就是尾部删除数据快,来看看为什么快
实现要点:删除数据你的_size有效元素个数肯定要减一,但是在最开始你得现有数据才能删除吧
//尾部删除
void seqlist_pop_back(SeqList* slptr)
{
//要进行尾删的顺序表存在并且最少有一个数据你才能删除
assert(slptr);
assert(slptr->_size > 0);
//尾部删除最简单,只需要移动_size的位置,把_size所处位置标记为未使用就可以了
//注意计算机中的删除可不是真正的删除,扔了清空然后啥也没有了
//而是把原有数据覆盖,或者是flag = 0,把这块空间标记为未使用!
slptr->_size--;
}
2.6->顺序表增删查改操作之头部插入
要点:要在顺序表的头部插入数据,那么就需要先把顺序表原有的数据从后往前依次向后挪动一位,最后再将数据插入到下标 = 0的位置,插入数据了那你的_size有效元素个数也得增加对吧
//进行尾部插入, 尾部删除, 头部插入, 头部删除
//顾名思义,就是在顺序表头部插入数据,头部删除数据 即 下标 = 0位置插入/删除数据
//顺序表尾部插入数据,尾部删除数据,即下标 = slptr->_size 的位置插入/删除数据
//其实头部删除和插入没有意义,想象一个场景,我已经有10w个数据了,
//就因为要插入或者删除你一个数据我就要把10w个数据移动一次,效率太低了吧!!!
//头部插入
void seqlist_push_front(SeqList* slptr,int x)
{
//判断顺序表存在并且检查顺序表的容量
assert(slptr);
SeqList_check_capacity(slptr);
//开始插入操作
int end = slptr->_size - 1;
while (end >= 0)
{
slptr->_arr[end+1] = slptr->_arr[end];
end--;
}
slptr->_arr[0] = x;
slptr->_size++;
}
2.7->顺序表增删查改操作之头部删除
要点:要在头部删除数据首先保证头部有数据,然后从前边元素(下标==1位置)到后边元素依次向前推进一个位置,最后_size有效元素个数减一
//头部删除
void seqlist_pop_front(SeqList* slptr)
{
//确保顺序表存在并且里边至少有一个数据
assert(slptr);
assert(slptr->_size > 0);
int begin = 0;
while (begin < slptr->_size - 1)
{
slptr->_arr[begin] = slptr->_arr[begin + 1];
begin++;
}
slptr->_size--;
}
2.8->顺序表增删查改操作之查找元素
这个很简单,直接上代码
//查找某个数据
int seqlist_search(SeqList* slptr, int x)
{
assert(slptr);
int i = 0;
for (i = 0; i < slptr->_size; i++)
{
if (slptr->_arr[i] == x)
{
return i;//找到了,返回下标
}
}
return -1;//找不到就返回-1
}
2.9->顺序表增删查改操作之指定位置插入数据
在指定位置插入数据,但是也要大量移动数据
//指定位置插入数据
void seqlist_insert(SeqList* slptr,int pos,int x)
{
assert(slptr);
assert(pos >= 0 && pos <= slptr->_size);
SeqList_check_capacity(slptr);
int end = slptr->_size - 1;
while (end >= pos)
{
slptr->_arr[end + 1] = slptr->_arr[end];
end--;
}
//这里就可以插入
slptr->_arr[pos] = x;
slptr->_size++;
}
2.10->顺序表增删查改操作之指定位置删除数据
指定位置删除数据,同样需要移动大量数据
//指定位置删除数据
void seqlist_earse(SeqList* slptr, int pos)
{
assert(slptr);
assert(slptr->_size > 0);
assert(pos >=0 && pos <slptr->_size);
int begin = pos;
while (begin < slptr->_size - 1)
{
slptr->_arr[begin] = slptr->_arr[begin + 1];
begin++;
}
slptr->_size--;
}
3->您的专属鼓励师
一遍学不会没关系吖,多看几遍,我也是学了好多遍呢,小伙伴们肯定学的又快又好!!!最后希望写的内容对小伙伴们有所帮助,我写的如果有哪里不对的地方请在评论区或者私信指出来哦!让我们一起进步吖,任何疑问包括心情不好都可以找我聊聊,我很乐意当你的倾听者吖.