目录
1.什么是顺序表
1.1顺序表的定义
顺序表是用一段物理内存连续的存储单元依次存储数据结构元素的线性结构,它的底层其实是数组。
1.2顺序表与数组的区别
顺序表其实就是在数组的基础上,进行了一系列对数组元素的操作,实现了对数组的封装,完成了诸如增删查改的功能。
1.3顺序表的分类
顺序表可分为静态顺序表和动态顺序表
1.3.1静态顺序表
概念:存储定长数组的元素
定义如下图所示:
这种顺序表的定义方式缺陷也是非常明显的,就是空间给少了不够用,给多了就会浪费
1.3.2动态顺序表
概念:按照需求申请空间的顺序表
定义方式:
这里要解释一下typedef关键字。
2.顺序表的功能
顺序表的功能即对存储的数据完成增加、删除、查找、改变等功能。
下面我们会着重实现这些功能
3.功能的具体实现
动态顺序表和静态顺序表在实现逻辑上是大同小异的,在这里我们实现一个动态顺序表。
3.0初始化顺序表
顺序表的初始化,就相当于我们之前学习的给变量初始化。这里不过多解释
//通过实参和形参以及函数栈帧解释为什么传指针
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
//以上两行可以用这行代码替代
//ps->size=ps->capacity=0;
}
3.1打印数据
顺序表的数据打印本质就是数组的数据打印,这里可以根据情况判断是否要解释打印方式。
void SLPrint(SL ps)
{
//.操作符用于直接访问
//->操作符用于间接访问(指针)
for (int i = 0; i < ps.size; i++)
{
printf("%d", ps.a[i]);
}
}
3.2检测空间
由于我们使用的是动态顺序表,在每次增加数据的时候都需要检测一下是否已经到了存储容量,因此我们就有必要写一个检测空间的函数。
如果没有到达存储容量,那么我们即可继续进行程序下面的代码。
如果已经到达了存储容量,那么我们就需要扩容了。
首先,我们扩容需要判断一下我们原来的存储容量是多大。
如果是0的话我们就需要给它赋一个值,否则的话我们就将原存储空间扩大到原来的二倍即可。
因此我们可以在这里使用一个三目表达式来完成上述逻辑。
在存储空间已经扩大之后,我们就要操纵我们实际的数组的空间了。在这里,我们可以使用一个realloc扩大原来a的空间。 realloc函数见此篇
之后我们就要更新我们的capacity和a了。
//通过实参和形参以及函数栈帧解释为什么传指针
void SLCheckCapacity(SL* ps)
{
//插入数据之前先看空间够不够
if (ps->capacity == ps->size)
{
//这行代码可以先写一个if-else判断语句,之后再优化为三目。
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//由于realloc函数返回值为void* 所以需要写一个值接受返回值
SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
if (!tmp)
{
perror("realloc fail!");
exit(1);//直接退出程序
}
//空间申请成功
ps->capacity = newcapacity;
ps->a = tmp;
}
}
3.3尾插数据
尾插数据,就是在最后一个数据之后插入数据。
也就是说,在数组最后一个数据的后面增加一个数据即可。
//这里要有两个参数,一个是指针,另外一个是要插入的数据
void SLPushBack(SL* ps, SLDataType x)
{
//首先应判断一下空间是否满了
SLCheckCapacity(&ps);
//之后就可以插入数据了
ps->a[ps->size] = x;
//更新size
ps->size+1;
//以上两行代码可用这行代码替代
//ps->a[ps->size++] = x;
//原因是后置++是先使用再++。我们会先赋值再size++。
}
3.4尾删数据
尾删数据,即删除最后一个数据。
数组删除最后一个数据,将最后一个数据置0,然后size--即可。
但是还有一个办法,我们直接size--。
void SLPopBack(SL* ps)
{
//直接--,size会减1.
//我们再用别的函数,用的都是新的size
//再增加数据也会直接改掉这个地方的值
--ps->size;
}
3.5头插数据
头插数据,即在顺序表的头部插入一个数据
那么,我们需要做的就是把原有的数据往后挪动1,之后再将第一个数据置为要插入的数据即可。
那么,怎么挪动呢?
void SLPushFront(SL* ps, SLDataType x)
{
//检查空间
SLCheckCapacity(&ps);
//整体后挪动
for (int i = ps->size; i >= 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
3.6头删数据
刚刚我们已经讲了删除数据,现在我们再来讲一下头删数据。
删除数据的时候,我们是直接size--,现在怎么样删除呢?是不是还可以通过覆盖来删除数据呢?
我们如果从第二个数据开始,把数据整体往前挪动一位,是不是就可以覆盖掉第一个数据了呢?
void SLPopFront(SL* ps)
{
for (int i = 0; i <ps->size-1; i++)
{
ps->a[i] = ps->a[i + 1];
}
//更新数据
ps->size--;
}
3.6指定位置插入
修改位置插入,假设我们要在下标为3的地方插入一个数据。
那么我们只要以下标为3的元素为起点,将元素往后挪即可。
这里往后挪动还是从后往前依次挪。
void SLInsert(SL* ps, int pos, SLDataType x)
{
//空间检测
SLCheckCapacity(&ps);
for (int i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
//更新数据
ps->a[pos] = x;
ps->size++;
}
3.7指定位置删除
我们还是从指定位置开始,把后面的数据往前面覆盖即可。
void SLErase(SL* ps, int pos)
{
for (int i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
}ps->size--;
3.8查找指定值
查找一个值,我们要返回他的下标。
我们直接遍历数组即可。
如果找到了,我们就返回他的下标,如果没有找到,那么我们就返回-1.
//这个函数的运行逻辑要带着大家过一下,注意return逻辑的讲解
int SLFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
3.9指定位置修改
这里我们直接找到这个下标,然后把数值改掉即可。
void SLModify(SL* p, int pos, SLDataType x)
{
p->a[pos] = x; //修改pos下标处对应的数据
}
3.10回收空间
当我们不用这块开辟的空间时,我们要把这块空间还给操作系统,以便我们程序后续的运行不会出现空间不足的问题
void SLDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
SeqList.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<assert.h> #include"Contact.h" //定义顺序表的结构 //#define N 100 // 静态顺序表 //struct SeqList //{ // int arr[N]; // int size;//有效数据个数 //}; //typedef int SLDataType;//方便后续类型的替换 typedef peoInfo SLDataType; //动态顺序表 typedef struct SeqList { SLDataType* arr; int size; //有效数据个数 int capacity; //空间大小 }SL; //typedef struct SeqList SL; //顺序表初始化 void SLInit(SL* ps); //顺序表的销毁 void SLDestroy(SL* ps); void SLPrint(SL s); //头部插入删除 / 尾部插入删除 void SLPushBack(SL* ps, SLDataType x); void SLPushFront(SL* ps, SLDataType x); void SLPopBack(SL* ps); void SLPopFront(SL* ps); //指定位置之前插入/删除数据 void SLInsert(SL* ps, int pos, SLDataType x); void SLErase(SL* ps, int pos); int SLFind(SL* ps, SLDataType x);