文章目录
前言
一、顺序表
1.灵魂三连击:咋来的?啥玩意?有啥用?
先来说一说咋来的: 你敲代码需要定义数据(字符串、数组、阿巴阿巴)吗?
所以理所应当就会有线性表这个概念??????线性表(linear list)是n个具有相同特性的数据元素的有限序列。顺序表也是线性表的一种,最常见的数组也算~~~算吗?我也具体忘记了,这无伤大雅!
再来说一说啥玩意: 顺序表就是数据按顺序结构在一段连续的空间上进行存储!
顺序表有什么卵用?: 对连续的数据进行操作。以满足实际工作中的需求,比如需要在连续顺序的数据中插入一个数,变成这样
2.这玩意这么实在,怎么用?
当然就是要创建了!!顺序表分为静态和动态两种,静态的话参考数组,没啥说的,如果有,那就不说了
3.那就来看看动态顺序表是个什么东西?如何创建?
动态顺序表------底层空间从堆上动态申请出来的! 底层空间?堆?可以看看这个文章C 语言函数中栈帧的创建与销毁
typedef struct SeqList { //自定义结构体SeqList,里面包含三个变量
DataType* array; //指向存储元素空间的起始位置
int capacity; //表示空间总的大小
int size; //有效元素的个数
}SeqList;
//对array,capacity,size进行初始化设置
//Struct SeqList S;
// S.array :10
// S.capacity :10
// S.size :
原理如下:
二、顺序表操作
1.操作声明
什么是对顺序表的操作?就是在上面定义好的顺序表中对其进行元素的增删查找。都有哪些呢?
//初始化ps(对指向顺序表的指针进行初始化操作)
void SeqListInit(SeqList* ps, int initCapacity);
//销毁(对顺序表进行销毁)
void SeqListDestory(SeqList* ps);
//尾插(在顺序表最后面加入元素)
void SeqListPushBack(SeqList* ps, DataType data);
//尾删(删除顺序表最后的元素)
void SeqListPopBack(SeqList* ps);
//头插(在顺序表最前面加入元素)
void SeqListPushFront(SeqList* ps, DataType data);
//头删(删除顺序表第一个元素)
void SeqListPopFront(SeqList* ps);
//在顺序表的pos位置插入元素data
// 注意pos的范围必须在[0,ps->size]
void SeqListInsert(SeqList* ps, int pos, DataType data);
//删除pos位置的值
// 注意pos范围
void SeqListErase(SeqList* ps, int pos);
//有效元素的个数
int SeqListSize(SeqList* ps);
//空间总的大小
int SeqListCapacity(SeqList* ps);
//返回空间中某个元素的位置
void SeqListFind(SeqList* ps, DataType data);
//检查顺序表是否为空
int SeqListEmpty(SeqList* ps);
//获取顺序表中第一个元素
DataType SeqListFront(SeqList* ps);
//获取顺序表中最后一个元素
DataType SeqListBack(SeqList* ps);
//获取顺序表任意一个位置元素
DataType SeqListGet(SeqList* ps,int pos);
//打印顺序表
void SeqListPrint(SeqList* ps);
//测试顺序表
void TestSeqList();
2.具体操作定义
初始化: 先从堆上申请一块内存空间用以存储数组。先利用malloc申请空间initCapacity,
//将ps指向的结构体变量初始化成功
void SeqListInit(SeqList* ps, int initCapacity) { //检测一下指针参数ps,防止取空
assert(ps);
ps->array = (int*)malloc(initCapacity * sizeof(int));//申请一块内存用以村存储数组
//检测生成是否成功
if (NULL == ps->array) { //检测指针ps指向申请的内存空间中存放的数组首元素地址是否存在
assert(0);
return;
}
ps->capacity = initCapacity; //指针ps指向的capacity初始化为申请的内存空间
ps->size = 0; //初始化内存空间中的元素个数
}
销毁: 销毁顾名思义就是之前在堆上申请了内存空间用以存储顺序表,现在这个顺序表结束了它的使命,就要将申请的空间进行释放,否则久而久之,申请的内存空间过多而没有销毁会出现所谓的内存泄漏
//将ps指向的结构体变量销毁
void SeqListDestory(SeqList* ps){
assert(ps);
if (ps->array) {
free(ps->array);//释放申请的内存
ps->array = NULL;//释放后,数组清空
ps->capacity = 0;//申请的内存空间清零
ps->size = 0;//元素个数清零
}
}
尾插: 这里需要注意的是,你要在array的末尾插入元素,得有地方给你插,因为原来capacity已经给定了空间为10,array有10个元素数量。能插的地方有10个,10个都满了,需要扩容才能继续在后面插入元素。
//尾插
void SeqListPushBack(SeqList* ps, DataType data) {
assert(ps);
//1.检查空间是否足够
CheckCapacity(ps);
//2.在最后一个元素之后插入data
ps->array[ps->size] = data;//ps指向size的位置恰好是元素个数,同时是尾插的存储位置
ps->size++;//由于加入一个元素,导致size增加1
}
检查空间是否足够: 这里不用多说,尾插中由于需要有足够的空间,所以要检查
//检查容量进行扩容
void CheckCapacity(SeqList* ps) {
assert(ps);
if (ps->size == ps->capacity) { //元素的个数和申请的内存空间个数相等,说明需要扩容
int newCapacity = (ps->capacity << 1);//定义一个新的空间newCapacity在原空间上左移1(左移扩大两倍)
DataType* temp = (DataType*)malloc(sizeof(DataType) * newCapacity);//为newCapacity申请内存空间,指针temp
if (NULL == temp) {
exit(0);
return;
}
//memcpy
for (int i = 0; i < ps->size; ++i) { //令指针temp代替指针原指针ps的位置
temp[i] = ps->array[i];
}
//对原内存空间进行释放,使用新的内存空间
free(ps->array);
ps->array = temp;
ps->capacity = newCapacity;
}
}
尾删: 尾删很简单,零数组array的size数量减 1 就行。不过在这里需要检测顺序表是否为空,空的话拿头给你删?
//尾删
void SeqListPopBack(SeqList* ps) {
assert(0);
//1.要检查是否为空,为空直接返回,有元素删除
//if (0 == ps->size) {
// return;
//}
//添加函数检测顺序表是否为空
if (SeqListEmpty(ps)) {
return;
}
ps->size--;
}
检测顺序表是否为空(判空): 这里就是对尾删里面那个判空进行的填坑。
//判空
int SeqListEmpty(SeqList* ps) {
assert(ps);
return 0 == ps->size;//size如果等于0的话,与0相等,此表达式为真。返回真
}
头插: 与尾插类似,不同点在于需要对整个元素向后进行搬移,然后第一个位置空出来进行头插
//头插
void SeqListPushFront(SeqList* ps, DataType data) {
assert(ps);
//1.检测空间是否足够
CheckCapacity(ps);
//2.将顺序表中的所有元素整体向后搬移一位
for (int i = ps->size - 1; i >= 0; --i) {
ps->array[i + 1] = ps->array[i]; //原来array[i]的位置变成现在array[i+1]的位置
}
//3.插入元素
ps->array[0] = data;
ps->size++;
}
头删:
//头删
void SeqListPopFront(SeqList* ps) {
//判空
if (SeqListEmpty(ps)) {
return;
}
//将顺序表中除第一个元素外往前搬
for (int i = 0; i < ps->size - 1; i++) {
ps->array[i] = ps->array[i + 1];
}ps->size--;
}
在顺序表的pos位置插入元素data:(注意pos的范围必须在[0,ps->size]) 这里的原理就是把pos位置前的元素不管,然后,pos位置之后包括pos位置的元素进行头插
void SeqListInsert(SeqList* ps, int pos, DataType data) {
//1.参数检测
assert(ps);
if (pos < 0 || pos > ps->size) {
printf("pos的位置有问题");
return;
}
//2.检测是否需要扩容
CheckCapacity(ps);
//3.插入元素
for (int i = ps->size - 1; i >= pos; i--) {
ps->array[i+1] = ps->array[i];
}
ps->array[pos] = data;
ps->size++;
}
删除pos元素位置的值: 同样的,pos之后的元素向前进行头删
void SeqListErase(SeqList* ps, int pos) {
assert(ps);
if (pos < 0 || pos >= ps->size) {
printf("位置有问题");
return;
}
if (SeqListEmpty(ps)) { //判空,元素不能为空,才能进行某一位置的删除
return;
}
for (int i = pos + 1; i < ps->size; ++i) {
ps->array[i - 1] = ps->array[i];
}
ps->size--;
}
其他: 包括有效元素个数的定义(定义顺序表中有几个有效元素),空间总的大小定义,返回空间中某个元素的位置(从前往后进行遍历,遍历的值 i == data时,输出 i),获取顺序表中的元素(首元素,尾元素,任意位置元素),打印顺序表,测试顺序表。这些都比较简单,统一列举出来
//有效元素的个数
int SeqListSize(SeqList* ps) {
assert(ps);
return ps->size; //有效元素个数就是size之前的所有 array 中的元素
}
//空间总的大小
int SeqListCapacity(SeqList* ps) {
assert(ps);
return ps->capacity; //空间总的大小就是 capacity
}
//返回空间中某个元素的位置
int SeqListFind(SeqList* ps, DataType data) {
assert(ps);
for (int i = 0; i < ps->size; i++) { //i从0到有效元素进行遍历,
if (ps->array[i] == data) { //遍历到的值 == data 时,得到位置i
return i;
}
}return -1;
}
//获取顺序表中第一个元素
DataType SeqListFront(SeqList* ps) {
assert(ps);
return ps->array[0];
}
//获取顺序表中最后一个元素
DataType SeqListBack(SeqList* ps) {
assert(ps);
return ps->array[ps->size - 1];
}
//获取顺序表任意一个位置元素
DataType SeqListGet(SeqList* ps, int pos) {
assert(ps);
return ps->array[pos];
}
//打印顺序表
void SeqListPrint(SeqList* ps) {
assert(ps);
for (int i = 0; i < ps->array; ++i) {
printf("%d ", ps->array[i]);//打印数组中所有的元素
}printf("\n");
}
总结
上面总结的很详细了,还总结??我跟在座的各位总结个