目录
一、前言
🍎什么是数据结构
🍐学习数据结构有什么用
二、顺序表概念 ------- (线性表)
🍉什么是线性表
🍓顺序表概念详解
💦 顺序表与数组的区别
三、顺序表详解
🍌顺序表结构
💦静态顺序表
💦动态顺序表
🍊动态顺序表各个接口的实现
⭕ 接口1:定义结构体 SL
⭕ 接口2:初始化结构体 (SLInit)
⭕ 接口3:检查结构体中的数组是否需要扩容(SLCheckCapacity)
⭕ 接口4:尾插 (SLPushBack)
⭕ 接口5:尾插删 (SLPopBack)
⭕ 接口6:头插 (SLPushFront)
⭕ 接口8:在指定位置插入数据(SLInsert)
⭕ 接口9:在指定位置删除数据(SLErase)
⭕ 接口10:查找某一个数据的位置(SLFind)
⭕ 接口11:查找某一个数据的起始位置(SLFinds)
⭕ 接口13:销毁(SLDestory)
四、顺序表的完整代码
🍇SeqList.h
🍋Seqlist.c
🥝test.c
🍍代码运行的菜单界面
五、 共勉
一、前言
在我们学习顺序表之前呢,我们大家肯定会有疑问,什么是数据结构,为什么要去学习数据结构,顺序表在数据结构里面代表什么呢?,这里我来给大家一次解惑,让大家真正的搞懂数据结构,学习起来才更加有动力。
🍎什么是数据结构
在面对数据结构,许多同学肯定会感到头疼,因为它很麻烦,那么到底什么是数据结构呢?
大家可以这样去思考:
在平时的学习中,经常用什么方式把 数据 放在一起?
有的老铁可能会说:变量(差的有点远)
还有的老铁会说是:数组 (差不多) :数组 能够看成 一种 数据结构的形式
我的理解: 数据结构就是一群数据按照一定的结构组合在一起就可以理解为 数据结构
官方解释:数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的 数据元素集合 叫做:数据结构
常见的数据结构的体现: 有线性结构、树形结构、图形结构、等
🍐学习数据结构有什么用
大家可以继续思考,学习数据结构能干什么?
有的老铁可能会说:将一些四处 飘泊 的数据收集起来 (有道理)
还有的老铁会说是:将这些凌乱的数据,整理一下,变得有规律,方便查找 (很有道理)
我的理解 :既然说的是 结构 ,我们肯定可以通过结构,进行数据的查询、数据的创建、进行一系列的 增删改查 的操作。
二、顺序表概念 ------- (线性表)
🍉什么是线性表
本次我们要来介绍,第一种数据结构,也是数据结构中最简单的一部分:线性表
此时此刻,肯定会有老铁会有疑问,为什么难道他会像一条线一样一次连接吗?
没错,当数据首尾一次相连接,这种数据结构就被叫做线性表。
注意 :相连接是逻辑上的连接,不是空间上的连接。
此时,可能还会有老铁站出来说:你说的这不就是数组嘛,小意思,我会。
其实 数组只是一种线性表,可是线性表还有很多种形式哦!
线性表的形式: 数组、链表、顺序表、队列等 (其中数组我们在C语言已经学过,不再提及) 。
🍓顺序表概念详解
在讲解是是顺序表之前,肯定会有老铁再问,顺序表和数组,都是 线性表 中最简单的体现,那它们之间有什么区别呢?
💦 顺序表与数组的区别
顺序表:顺序表是用 一段物理地址连续存储单元 ,依次存储数据元素的线性结构
解释:顺序表就是将数据连着存放,存放的数据空间也是连着的
数组:数据不用按顺序的存放,可以随意的存放
区别:数组不按空间顺序随意地存放数据,但是顺序表的数据是要按照空间的的顺序存放
说的这里估计有些老铁已经开始有点 晕晕的状态了,我来给大家画图解释一下:
三、顺序表详解
🍌顺序表结构
💦静态顺序表
知识点1:静态顺序表
静态顺序表:使用定长数组存储
优点:操作简单,代码实现容易
缺点:定长数组很受限制,数组开小了不够用,开大了又浪费
💦动态顺序表
知识点2:动态顺序表
动态顺序表:使用动态开辟的数组进行数据的存储
优点:数组可以根据自己的需求进行调解
缺点:代码过于麻烦
由于在我们平时的代码需求中,动态顺序表的的需求是很高的,所以今天以动态顺序表为例,来进行代码书写。
🍊动态顺序表各个接口的实现
这里先建立三个文件:
1️⃣ :SeqList.h文件,用于函数声明
2️⃣ :SeqList.c文件,用于函数的定义
3️⃣ :Test.c文件,用于测试函数
建立三个文件的目的: 将顺序表作为一个项目来进行书写,方便我们的学习与观察。
⭕ 接口1:定义结构体 SL
🔑在动态顺序表的结构由 三个成员组成:
▶ 指向动态开辟空间的 a 指针
▶ 记录当前有效数据个数的 size 变量
▶ 记录当前容量的 capacity 变量
🔑 我们会发现,动态顺序表的结构体比静态顺序表的结构体多了一个 capacity 成员。
我们用这个成员记录当前顺序表的最大容量,当有效数据个数 size 和 capacity 相等时,说明了当前顺序表空间已满,需要进行扩容。
typedef int SLDateType; // 数据类型 typedef struct Seqlist { SLDateType* a; // 指向动态开辟的空间 int size; // 记录存储了多少个有效数据 int capacity; //空间容量大小 }SL;
首先:先定义初始的变量,并记录 空间中存储数据的个数 :举例 初始有三个数据
⭕ 接口2:初始化结构体 (SLInit)
知识点3:顺序表进行初始化
为什么要进行结构体的初始化呢?
在顺序表进行操作之前,需要先将其内容进行初始化,防止之后的操作出现错误
在实现初始化接口之前,大家先思考一下哈,参数可不可以 为结构体变量?
例如: ❌ 写法
void SLInit(SL ps) { ps.a = NULL; ps.sz = ps.capacity = 0; }
估计大部分老铁都会觉得这样是对滴,在这里我要告诉大家,这总写法是绝对行不通的!!
大家要知道,实参在传参的时候,会形成一个份临时拷贝,叫做形参。
当我们在函数中对形参的内容进行修改时,是不会影响到实参的,所以不可以。
正确的写法:
要正确的进行的初始化结构体,我们需要传递 SL 的地址,通过指针对结构体的内容进行修改。
传递 SL 的地址,通过指针的方式进行初始化结构体
函数原型:void SLInit(SL *ps) // 初始化函数实现: ✔ 写法
void SLInit(SL *ps) // 初始化 { assert(ps); // 断言 指针ps 防止出现空指针 ps->a = NULL; ps->size = 0; // 进行结构体的初始化 ps->capacity = 0; }
⭕ 接口3:检查结构体中的数组是否需要扩容(SLCheckCapacity)
🔑 当顺序表需要插入数据时,可能会遇到三种情况:
▶ 整个顺序表没有空间
▶ 空间不够,需要扩容
▶ 空间足够,直接插入数据
🔑 如果顺序表空间足够,那么不需要扩容,通过相关操作插入数据
🔑 如果空间不足或者根本没有空间,那么就得扩容。
▶ 当顺序表没有空间时,我们开辟四个空间
▶当顺序表空间不足,我们将当前空间扩大为两倍(扩两倍是为了防止扩容过度,或扩小了频繁扩容,消耗过大)。
▶当顺序表空间足够,不进行任何操作,if语句判断后,直接返回。
知识点4:给顺序表扩充容量
函数原型:void SLCheckCapacity(SL* ps)函数的实现:
注意:在扩容的的时候 我们用到了 动态分配函数 realloc ()函数 ,如果大家对这个函数不太了解,请看我的上一篇文章。
动态分配讲解:(55条消息) 动态内存分配:malloc、calloc、realloc(超详解析,多维度分析,小白一看就懂!!!!)_sunny-ll的博客-CSDN博客_calloc malloc realloc
//检查是否需要扩容 void SLCheckCapacity(SL* ps) { assert(ps); // 扩容 // 顺序表没有空间 或者 顺序表空间不足 if (ps->size == ps->capacity)//检查是否越界 { int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; SLDateType* temp = (SLDateType*)realloc(ps->a, newCapacity * sizeof(SLDateType)); //realloc 的用法 if (temp == NULL) { perror("realloc fail"); exit(-1); //异常终止 } ps->a = temp; ps->capacity = newCapacity; } }
⭕ 接口4:尾插 (SLPushBack)
知识点5:在顺序表的尾部插入数据
函数原型:void SLPushBack(SL* ps, SLDateType x) // 尾部插入数据
函数实现:
void SLPushBack(SL* ps, SLDateType x) // 尾部插入数据 { assert(ps); // 检查是否需要扩容 SLCheckCapacity(ps); // 进行尾部插入 ps->a[ps->size] = x; ps->size++; }
⭕ 接口5:尾插删 (SLPopBack)
知识点6:在顺序表的尾部删除数据
函数原型:void SLPopBack(SL* ps) //尾部删除数据
函数实现:
void SLPopBack(SL* ps) //尾部删除数据 { assert(ps); //ps->a[ps->size - 1] = 0; // size 是从 0 开始的 所以要减去 1 // 温柔的方式 /*if (ps->size == 0) { printf("顺序表已空\n"); return; }*/ // 暴力的方式 assert(ps->size > 0); ps->size--; }
🔑注意:当顺序表没有元素时,也就是 sz 为 0 时,不可以进行删除
🔑举例: 假如顺序表有 5 个数据,此时我们尾删 6 个数据,那么这时,我们顺序表的 size = -1。此时程序不会报错,因为本身就没有数据可以删除,但是让我们在进行尾插的时候,程序就会报错啦,因为尾插数据会访问 size 的下表 -1 此时出现了越界访问,程序出错。
🔑解决方案 :加入断言进行判断,防止size 的下标越界
⭕ 接口6:头插 (SLPushFront)
知识点7:在顺序表的头部插入数据
函数原型:void SLPushFront(SL* ps, SLDateType x)
头插原理 :头插就是将数据放在下标为 0 的地方 其余数据依次向后挪一位,此时出现两种情况:
再向后挪数据的过程中
▶ 先从第一个数据开始挪动
▶ 先从最后一个数据开始挪动
函数图解:情况(1)先从第一个数据开始挪动 :会发生 数据覆盖 ❌
函数图解:情况(2)先从最后一个数据开始挪动 :符合我们的理想状态 ✔
函数实现:
void SLPushFront(SL* ps, SLDateType x) { assert(ps); // 扩容 SLCheckCapacity(ps); // 挪动数据 int end = ps->size - 1; while (end >= 0) { ps->a[end + 1] = ps->a[end]; end--; } ps->a[0] = x; ps->size++; }
⭕ 接口7:头删(SLPopFront)
知识点8:在顺序表的头部删除数据
函数原型:void SLPopFront(SL* ps)
头删原理:头删就是将下标为 0 的数据删除,其余数据依次向前挪动,此时同样出现两情况。在向前挪动的过程中
▶ 先从下标为 1 的数据向前挪动
▶ 先从最后一个数据向前挪动
函数图解:情况(1):先从最后一个数据开始挪动 ❌
函数图解:情况(2):先从小标为 1 的数据开始挪动
函数实现:
void SLPopFront(SL* ps) { assert(ps); assert(ps->size > 0); //暴力检查 int begin = 1; while (begin < ps->size) { ps->a[begin - 1] = ps->a[begin]; begin++; } ps->size--; }
⭕ 接口8:在指定位置插入数据(SLInsert)
知识点9:在顺表的指定的 pos 下标处插入数据
函数的原型:void SLInsert(SL* ps, int pos, SLDateType x)
注意:要实现这一功能,我们依然需要一个
end
下标,数据从后往前依次后挪,直到pos
下标移动完毕。另外,别忘了检查容量。函数的实现:
void SLInsert(SL* ps, int pos, SLDateType x) { assert(ps); assert(pos >= 0); // pos 的位置,因该在有效位置 assert(pos <= ps->size); // 等于 “=” size 相当于尾插 SLCheckCapacity(ps); // 扩容 int end = ps->size - 1; while (end >= pos) { ps->a[end + 1] = ps->a[end]; end--; } ps->a[pos] = x; ps->size++; }
函数拓展:该功能其实也可以实现头插和尾插,所以我们可以在
头插
和尾插
中复用该功能// 头插 void SeqListPushFront(SL* ps, SLDataType x) { SeqListInsert(ps, 0, x); } // 尾插 void SeqListPushBack(SL* ps, SLDataType x) { SeqListInsert(ps, ps->sz, x); }
⭕ 接口9:在指定位置删除数据(SLErase)
知识点10:顺序表在指定的 pos 下标删除数据
函数原型:void SLErase(SL* ps, int pos)
注意:要实现这一功能,我们需要一个
begin
下标,数据从前往后依次前挪,直到sz-1
下标移动完毕。函数实现:
void SLErase(SL* ps, int pos) { assert(ps); assert(pos >= 0); assert(pos < ps->size); assert(ps->size > 0); //检查 size 是否为空 int begin = pos + 1; while (begin < ps->size) { ps->a[begin - 1] = ps->a[begin]; begin++; } ps->size--; }
函数拓展:同样的,该接口也可复用于
头删
和尾删
:// 头删 void SeqListPopFront(SL* ps) { SeqListErase(ps, 0); } // 尾删 void SeqListPopBack(SL* ps) { SeqListErase(ps, ps->sz - 1); }
⭕ 接口10:查找某一个数据的位置(SLFind)
知识点11:查找某一个数据的位置
函数的原型:int SLFind(SL* ps, SLDateType x)
函数的实现:
int SLFind(SL* ps, SLDateType x) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->a[i] == x) { return i; } } return -1; }
⭕ 接口11:查找某一个数据的起始位置(SLFinds)
知识点12 :查找某一个数据的起始位置
函数原型:int SLFinds(SL* ps, SLDateType x, int begin)
函数实现:
int SLFinds(SL* ps, SLDateType x, int begin) { assert(ps); for (int i = begin; i < ps->size; i++) { if (ps->a[i] == x) { return i; } } return -1; }
⭕ 接口12:打印函数(SLPrint)
知识点13:打印已经实现的函数
函数原型:void SLPrint(SL *ps)
函数实现:在每次操作后,可以打印出顺序表,观察操作情况
void SLPrint(SL *ps) // 打印函数 { assert(ps); for (int i = 0; i < ps->size; i++) { printf("%d ", ps->a[i]); } printf("\n"); }
⭕ 接口13:销毁(SLDestory)
知识点14:销毁顺序表所开辟的空间,防止内存泄漏
函数原型:void SLDestory(SL* ps)
函数实现:void SLDestory(SL* ps) //销毁 { assert(ps); if (ps->a != NULL) { free(ps->a); ps->a = NULL; ps->size = ps->capacity = 0; } }
四、顺序表的完整代码
🍇SeqList.h
#pragma once // 防止重复包含 //---------动态顺序表------------// #include <stdlib.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> #include <assert.h> #define M 1000 // 加入typedef 可以进行简写 typedef int SLDateType; // 数据类型 typedef struct Seqlist { SLDateType* a; int size; // 记录存储了多少个有效数据 int capacity; //空间容量大小 }SL; void SLInit(SL* ps); //初始化 void SLDestory(SL* ps); //销毁 void SLPushBack(SL* ps, SLDateType x); // 尾插 void SLPrint(SL* ps); //打印函数 void SLPopBack(SL* ps); //尾删 void SLPushFront(SL* ps, SLDateType x); //头插 void SLPopFront(SL* ps); //头删 void SLCheckCapacity(SL* ps); //检查是否需要扩容 //中间插入删除 void SLInsert(SL* ps, int pos, SLDateType x); //在pos位置插入数据 void SLErase(SL* ps, int pos); //删除pos位置的数据 void SLsize(SL* ps); // 测试 size 的值 int SLFind(SL* ps, SLDateType x); //查找某一个数字的位置 int SLFinds(SL* ps, SLDateType x, int begin); // 确定查找数字的起始位置
🍋Seqlist.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "SeqList.h" void SLPrint(SL *ps) // 打印函数 { assert(ps); for (int i = 0; i < ps->size; i++) { printf("%d ", ps->a[i]); } printf("\n"); } // 测试 size 的值 void SLsize(SL* ps) { assert(ps); printf("%d\n", ps->size); } void SLInit(SL *ps) // 初始化 { assert(ps); ps->a = NULL; ps->size = 0; ps->capacity = 0; } //检查是否需要扩容 void SLCheckCapacity(SL* ps) { assert(ps); // 扩容 if (ps->size == ps->capacity)//检查是否越界 { int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; SLDateType* temp = (SLDateType*)realloc(ps->a, newCapacity * sizeof(SLDateType)); //realloc 的用法 if (temp == NULL) { perror("realloc fail"); exit(-1); //异常终止 } ps->a = temp; ps->capacity = newCapacity; } } void SLDestory(SL* ps) //销毁 { assert(ps); if (ps->a != NULL) { free(ps->a); ps->a = NULL; ps->size = ps->capacity = 0; } } void SLPushBack(SL* ps, SLDateType x) // 尾部插入数据 { assert(ps); // 检查是否需要扩容 SLCheckCapacity(ps); // 进行尾部插入 ps->a[ps->size] = x; ps->size++; } void SLPopBack(SL* ps) //尾部删除数据 { assert(ps); //ps->a[ps->size - 1] = 0; // size 是从 0 开始的 所以要减去 1 // 温柔的方式 /*if (ps->size == 0) { printf("顺序表已空\n"); return; }*/ // 暴力的方式 assert(ps->size > 0); ps->size--; } // 头插 O(N) 尽量用尾插 // 需要从最后一个数据开始依次向后挪动,空出第一个位置,保证头插的位置 void SLPushFront(SL* ps, SLDateType x) { assert(ps); // 扩容 SLCheckCapacity(ps); // 挪动数据 int end = ps->size - 1; while (end >= 0) { ps->a[end + 1] = ps->a[end]; end--; } ps->a[0] = x; ps->size++; } // 头删 O(1) // 需要从前往后挪 void SLPopFront(SL* ps) { assert(ps); assert(ps->size > 0); //暴力检查 int begin = 1; while (begin < ps->size) { ps->a[begin - 1] = ps->a[begin]; begin++; } ps->size--; } //在pos位置插入数据 void SLInsert(SL* ps, int pos, SLDateType x) { assert(ps); assert(pos >= 0); // pos 的位置,因该在有效位置 assert(pos <= ps->size); // 等于 “=” size 相当于尾插 SLCheckCapacity(ps); // 扩容 int end = ps->size - 1; while (end >= pos) { ps->a[end + 1] = ps->a[end]; end--; } ps->a[pos] = x; ps->size++; } //删除pos位置的数据 // 其中 SLErase(ps,0) 就是头删 // 其中 SLErase(ps,ps-size-1) 就是尾删 void SLErase(SL* ps, int pos) { assert(ps); assert(pos >= 0); assert(pos < ps->size); assert(ps->size > 0); //检查 size 是否为空 int begin = pos + 1; while (begin < ps->size) { ps->a[begin - 1] = ps->a[begin]; begin++; } ps->size--; } // 查找某一个数据的位置是 int SLFind(SL* ps, SLDateType x) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->a[i] == x) { return i; } } return -1; } // begin 查找 x 的起始位置 int SLFinds(SL* ps, SLDateType x, int begin) { assert(ps); for (int i = begin; i < ps->size; i++) { if (ps->a[i] == x) { return i; } } return -1; }
🥝test.c
#define _CRT_SECURE_NO_WARNINGS 1 // ----------- 顺序表 ----------- // // 顺序表:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构, // 一般情况下采用数组存储。在数组上完成数据的增删查改。 // 重点:要求连续存储 与数组的区别 :数组是随意存储 // 而顺序表 是连续的存储必须是挨着的 // 1.静态顺序表 --- 不太实用 // 对于数组开打了,浪费 。开少了,不够用 // 2.动态顺序表 --- 按需扩展空间 #include "SeqList.h" // 头插测试 void TestSeqList3() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushFront(&s1, 1); SLPushFront(&s1, 2); SLPushFront(&s1, 3); SLPushFront(&s1, 4); SLPrint(&s1); // 4 3 2 1 SLDestory(&s1); } //头删测试 void TestSeqList4() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushFront(&s1, 1); SLPushFront(&s1, 2); SLPushFront(&s1, 3); SLPushFront(&s1, 4); SLPrint(&s1); SLPopFront(&s1); SLPopFront(&s1); SLPopFront(&s1); SLPopFront(&s1); //SLPopFront(&s1); SLPrint(&s1); SLPushFront(&s1, 9); SLPrint(&s1); SLDestory(&s1); } // 测试 size 的值 // 注意 size 的值是从 0 开始的 void TestSeqList5() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushFront(&s1, 1); SLPushFront(&s1, 2); SLPushFront(&s1, 3); SLPushFront(&s1, 4); SLPrint(&s1); // 4 3 2 1 SLsize(&s1); // 4 也就是说,size 占了 5 个位置 SLDestory(&s1); } // 测试任意位置的插入SLInsert void TestSeqList6() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushBack(&s1, 1); SLPushBack(&s1, 2); SLPushBack(&s1, 3); SLPushBack(&s1, 4); SLPrint(&s1); // 1 2 3 4 SLInsert(&s1, 2, 20); SLPrint(&s1); // 1 2 20 3 4 SLInsert(&s1, 5, 500); SLPrint(&s1); // 1 2 20 3 4 500 //尾插 SLInsert(&s1, 0, 400); // 400 1 2 20 3 4 500 // 头插 SLPrint(&s1); SLDestory(&s1); } // 测试任意位置的删除 SLErase void TestSeqList7() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushBack(&s1, 1); SLPushBack(&s1, 2); SLPushBack(&s1, 3); SLPushBack(&s1, 4); SLPrint(&s1); // 1 2 3 4 SLErase(&s1, 2); SLPrint(&s1); // 1 2 4 SLErase(&s1, 2); SLPrint(&s1); // 1 2 SLErase(&s1, 0); SLPrint(&s1); // 2 SLDestory(&s1); } // 测试查找某一个数字,并将这个是数字删除 void TestSeqList8() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushBack(&s1, 1); SLPushBack(&s1, 2); SLPushBack(&s1, 3); SLPushBack(&s1, 4); SLPushBack(&s1, 5); SLPushBack(&s1, 6); SLPushBack(&s1, 7); SLPushBack(&s1, 8); SLPrint(&s1); // 1 2 3 4 5 6 7 8 int pos = SLFind(&s1, 5); if (pos != -1) { SLErase(&s1, pos); } SLPrint(&s1); // 1 2 3 4 6 7 8 SLDestory(&s1); } // 测试 删除顺序表中 所有的 5 void TestSeqList9() { SL s1; // 创建一个结构体变量 SLInit(&s1); // 把结构体变量的地址传过去 SLPushBack(&s1, 1); SLPushBack(&s1, 5); SLPushBack(&s1, 3); SLPushBack(&s1, 4); SLPushBack(&s1, 5); SLPushBack(&s1, 5); SLPushBack(&s1, 7); SLPushBack(&s1, 8); SLPrint(&s1); // 1 5 3 4 5 5 7 8 int pos = SLFinds(&s1, 5, 0); while (pos != -1) { SLErase(&s1, pos); pos = SLFinds(&s1, 5, pos); } SLPrint(&s1); // 1 3 4 7 8 SLDestory(&s1); } // 越界是不一定报错的 // 越界 读 基本都查不出来 只能是程序的值会出现值 // 越界 写 可能会报错 void menu() { printf("***********************************************************\n"); printf("1、尾插数据 2、尾删数据\n"); printf("\n"); printf("3、头插数据 4、头删数据\n"); printf("\n"); printf("5、在任意位置插入数据(位置3插入20)\n"); printf("\n"); printf("6、在任意位置删除数据 \n"); printf("\n"); printf("7、查找某个数据的位置,并删除它 \n"); printf("\n"); printf("8、删除顺序表中有的 某个数据 \n"); printf("\n"); printf("9、打印数据 \n"); printf("\n"); printf("-1、退出 \n"); printf("\n"); printf("***********************************************************\n"); } int main() { printf("************* 欢迎大家来到动态顺序表的测试 **************\n"); int option = 0; SL s; SLInit(&s); do { menu(); printf("请输入你的操作:>"); scanf("%d", &option); int sum = 0; int x = 0; int y = 0; int z = 0; int pos = 0; int w = 0; switch (option) { case 1: printf("请依次输入你要尾插的数据:,以-1结束\n"); scanf("%d", &sum); while (sum != -1) { SLPushBack(&s, sum); // 1.尾插 scanf("%d", &sum); } break; case 2: SLPopBack(&s); // 2.尾删 break; case 3: scanf("%d", &x); SLPushFront(&s, x); // 3.头插 break; case 4: SLPopFront(&s); // 4.头删 break; case 5: SLInsert(&s, 3, 20); // 5.在任意位置插入数据 break; case 6: SLErase(&s, 3); // 6.在任意位置删除数据 break; case 7: printf("请输入要删除序列的中的某个数字\n"); scanf("%d", &z); y = SLFind(&s, z); // 7.查找某个数字的位置,并且删除它 printf("%d的位置在%d处: \n", z, y); if (y != -1) { SLErase(&s, y); } break; case 8: printf("请输入要删除序列的中的数字\n"); //8.删除顺序表中 所有的 某个数据 scanf("%d", &w); pos = SLFinds(&s, w, 0); while (pos != -1) { SLErase(&s, pos); pos = SLFinds(&s, w, pos); } break; case 9: SLPrint(&s); break; default: if (option == -1) { exit(0); // 退出程序 } else { printf("输入错误,请重新输入\n"); } break; } } while (option != -1); // 退出程序 SLDestory(&s); return 0; }
🍍代码运行的菜单界面
五、 共勉
以下就是我对数据结构---顺序表的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对数据结构-------单链表的理解,请持续关注我哦!!!!!