news 2026/5/25 3:11:58

数据结构:线性表和顺序表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构:线性表和顺序表

一、线性表

线性表是一种逻辑结构,表示元素与元素之间的相邻关系,顺序表和链表是一种存储结构

第一个元素具有唯一后继,最后一个元素具有唯一前驱,中间的元素具有唯一的前驱和后继

二、顺序表

顺序表是线性表的顺序存储,用一段连续的内存空间依次存放数据元素

逻辑地址相邻的元素,内存地址也相邻

优点:支持随机访问;空间利用率高;尾插尾删效率高O(1)

缺点:扩容代价大;头插,按位置插,删除需要搬动数据

适用于:适用于需要大量随机访问,插入删除操作较少,就算需要这些操作也是在尾部进行

顺序表的分类:分为定容顺序表和可扩容顺序表

1. 顺序表的创建

1.1 定容顺序表

定长数组存储

typedef int Elementype; #define N 6 //定容顺序表结构体定义 typedef struct seqlist { //连续存储空间 int arr[N]; //有效元素长度 int length; //最大容量 int maxsize; }seqlist;

1.2 可扩容顺序表

动态开辟的数组存储

//可扩容顺序表 //类型重定义 typedef int Elementype; //初始格子数,初始化时malloc申请的格子数量 #define INTSIZE 10 //顺序表结构体定义 typedef struct seqlist { //内存连续的存储空间 Elementype* arr; //有效元素长度 int length; //最大容量 int maxsize; }seqlist;

2. 顺序表的初始化

//顺序表的初始化 void Init_seqlist(seqlist* plist) { //1.先安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.申请空间 plist->arr = (ElemType*)malloc(INTSIZE * sizeof(ElemType)); //判空 if (plist->arr == nullptr) exit(EXIT_FAILURE); //3.有效元素为0 plist->length = 0; //4.初始申请的空间个数为 plist->maxsize = INTSIZE; }

首先要对函数进行安全判断,DeBug版本用的是断言,release版本用的是if-else判空

然后用malloc在堆区购买一片连续的内存空间

一开始有效元素个数为0,容量为初始宏定义的个数

3. 顺序表扩容

//扩容 bool InCrease(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.购买新结点 ElemType* p = (ElemType*)realloc(plist->arr, plist->maxsize * 2 * sizeof(ElemType)); //3.判断空间是否申请成功,成功的话再将新申请的空间给arr if (p == NULL) return false; plist->arr = p; //更新容量 plist->maxsize = plist->maxsize * 2; return true; }

首先需要安全处理,然后使用新指针接收realloc购买2倍原来使用malloc和calloc购买的容量的大小(注意:这里不能是宏的二倍,因为如果是宏定义的二倍,会导致每一次扩容后的数量都是2*INTSIZE),判断空间申请是否成功,成功后再将地址传给顺序表,否则会导致顺序表丢失

4. 判满

//判满 bool IsFull(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.若有效元素个数==最大容量则已满 return plist->length == plist->maxsize; }

如果有效元素个数和申请的空间容量相等则已满,返回true

5. 判空

//判空 bool IsEmpty(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.若有效元素个数==0则为空 return plist->length == 0; }

如果有效元素个数==0则为空,返回true

6. 顺序表插入

6.1 头插

//顺序表头插 bool Insert_head(seqlist* plist,int val) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判满,若已满则扩容,如果扩容失败则退出 if (IsFull(plist)) { if (InCrease(plist) == false) return false; } //3.将原来顺序表中的元素从尾部开始挨个后移 memmove(plist->arr + 1, plist->arr, sizeof(ElemType) * plist->length); //4.再将新的元素插入arr[0] plist->arr[0] = val; //5.再更新有效元素个数 plist->length++; return true; }

首先安全处理

然后判满,如果已满扩容,如果扩容失败直接退出

使用memmove将元素从尾部挨个后移

memmove在处理内存折叠时:如果目标地址高于源地址,从高地址往低地址复制,避免未复制就被覆盖的现象

再将val插入空出来的地方

再更新有效元素个数

6.2 尾插

//顺序表的尾插 bool Insert_back(seqlist* plist, int val) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判满 if (IsFull(plist)) { if (InCrease(plist) == false) return false; } //3.尾部插入 plist->arr[plist->length] = val; //4.更新有效元素个数 plist->length++; return true; }

首先安全处理

然后判满,如果已满扩容,扩容失败则退出

然后直接在尾部插入val

插入完成后更新有效元素个数

6.3 按位置插

//按位置插 bool Insert_pos(seqlist* plist, int val, int pos) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判满 if (IsFull(plist)) { if (InCrease(plist) == false) return false; } //3.边界判断 if (pos > plist->length + 1 || pos < 0) return false; //4.在pos位置插入 //2 3 4 5 要在pos=3 下标2位置插入 7 //2 3 7 4 5 memmove(plist->arr + pos, plist->arr + pos - 1, sizeof(ElemType) * (plist->length - pos + 1)); plist->arr[pos - 1] = val; plist->length++; return true; }

首先进行安全判断

然后判满,如果已满扩容,扩容失败则退出

然后进行位置插入的边界值判断

然后将插入位置以及之后的元素,从最后一个元素开始,挨个往后移一位

将val插入

有效元素个数++

7. 顺序表删除

7.1 头删

//头删 bool delete_head(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判空 if (IsEmpty(plist)) return false; //3.将所有元素从前往后挨个前移一位 memmove(plist->arr, plist->arr + 1, sizeof(ElemType) * (plist->length - 1)); //4.更新有效元素个数 plist->length--; return true; }

首先安全判断

然后判断表是否为空,如果表为空直接退出

如果表不空,将元素从头开始挨个前移

然后将有效元素个数减一

7.2 尾删

//尾删 bool delete_back(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判空 if (IsEmpty(plist)) return false; //3.尾部删除,元素个数直接减一 plist->length--; return true; }

首先安全判断

然后判断表是否为空,如果表为空直接退出

如果表不空,然后将有效元素个数减一

7.3 按位置删

//按位置删 bool delete_pos(seqlist* plist, int pos) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判空 if (IsEmpty(plist)) return false; //3.判断要删除的位置的界限 if (pos < 1 || pos > plist->length + 1)return false; //4.从前往后,元素挨个前移 //2 3 4 5 要删除第3位的 memmove(plist->arr + pos - 1, plist->arr + pos, sizeof(ElemType) * (plist->length - pos)); //有效元素个数减1 plist->length--; return true; }

首先安全判断

然后判断表是否为空,如果表为空直接退出

如果表不空,将pos位置之后的元素从前往后依次前移一位

有效元素个数减一

7.4 按值删(只删除这个值出现的第一次)

//按值删(只删除这个值出现的第一次) bool Del_val_first(seqlist* plist,int val) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判空 if (IsEmpty(plist)) return false; //3.查找这个元素第一次出现的位置 int pos = search_seqlist(plist, val) + 1; //4.删除 memmove(plist->arr + pos - 1, plist->arr + pos, sizeof(ElemType) * (plist->length - pos)); plist->length--; return true; }

首先安全判断

然后判断表是否为空,如果表为空直接退出

如果表不空,查找要删除元素的下标(注意:此时返回的是下标,pos是当前元素的位置,所以需要+1,才是位置),然后赋值给pos

将pos位置之后的元素从前往后依次前移一位

有效元素个数减一

7.4 按值删(删除这个值出现的所有)

//按值删(只删除这个值出现的所有次) bool Del_val_ALL(seqlist* plist, int val) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.判空 if (IsEmpty(plist)) return false; //3.将不是val的值存入一个从头开始存 int len = 0; for (int i = 0; i < plist->length; i++) { if (plist->arr[i] != val) plist->arr[len++] = plist->arr[i]; } //4.更新有效元素个数 plist->length = len; return true; }

首先安全判断

然后判断表是否为空,如果表为空直接退出

如果表不空,申请一个新的计数器,将不是val的值从头存入arr

有效元素个数更新为计数器的值

8. 查找有效元素是否存在

遍历整个顺序表,有效元素存在返回元素下标,不存在返回-1

int search_seqlist(seqlist* plist, int val) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.遍历顺序表,查找有效元素 for (int i = 0; i < plist->length; i++) { if (plist->arr[i] == val)return i; } return -1; }

9. 清空

将有效元素个数置为0即可

//清空 void clear(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } plist->length = 0; }

10. 销毁

释放在堆区申请的空间,然后将plist->arr=nullptr,目的是为了避免二次释放,然后将有效元素个数和容量置为0即可

//销毁 void Dstory(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.释放堆空间 free(plist->arr); plist->arr = nullptr; plist->length = 0; plist->maxsize = 0; }

11. 打印顺序表

//打印函数 void show(seqlist* plist) { //1.安全处理 assert(plist != nullptr); if (plist == nullptr) { exit(EXIT_FAILURE); } //2.循环打印 printf("当前顺序表中的元素有:"); for (int i = 0; i < plist->length; i++) { printf("%d ", plist->arr[i]); } printf("\n"); }

12. 测试

int main() { seqlist s; Init_seqlist(&s); printf("length==%d maxsize==%d\n", s.length, s.maxsize); Insert_head(&s, 2); show(&s); Insert_back(&s, 3); Insert_back(&s, 4); Insert_back(&s, 5); Insert_back(&s, 3); Insert_back(&s, 4); Insert_back(&s, 5); Insert_back(&s, 3); Insert_back(&s, 4); Insert_back(&s, 5); show(&s); Insert_pos(&s, 7, 1); show(&s); delete_head(&s); show(&s); delete_pos(&s, 4); show(&s); Del_val_first(&s, 4); show(&s); Del_val_ALL(&s, 3); show(&s); clear(&s); printf("%d\n",s.length); Dstory(&s); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 3:07:26

告别无效背词,家门口的科学记忆工具太实用

养娃路上&#xff0c;很多焦虑都来自于“费力不讨好”的学习状态。在德州&#xff0c;无数家庭每天都在上演同样的画面&#xff1a;孩子埋头抄写单词数十遍&#xff0c;当天记得熟练&#xff0c;隔天全盘遗忘&#xff1b;家长反复听写、反复纠错&#xff0c;耗费大量时间精力&a…

作者头像 李华
网站建设 2026/5/25 3:06:42

C++ STL 容器详解:vector、list、map 与迭代器完全指南

目录 前言 一、STL 六大组件回顾 二、vector 容器详解 三、迭代器分类 四、list 容器详解 五、map 容器详解 六、总结 前言 STL 是 C 开发的核心利器&#xff0c;几乎所有项目都会用到容器、迭代器与算法。相比于 C 语言手动实现顺序表、链表、哈希结构&#xff0c;STL …

作者头像 李华
网站建设 2026/5/25 3:05:59

昇腾CANN ge 仓的图优化 Pass:哪些 Pass 真正影响推理性能

前言 你训练好一个模型&#xff0c;导出 ONNX&#xff0c;转成 CANN 的 OM 模型。推理时发现&#xff1a;延迟 89ms&#xff0c;吞吐只有 1200 samples/s。 你开始调优&#xff1a; 加 --op_precision_modeforce_fp16 → 延迟 72ms&#xff08;快 19%&#xff09;加 --auto_tun…

作者头像 李华
网站建设 2026/5/25 2:57:20

昇腾NPU强化学习训练实战——从PPO到GRPO的完整落地

强化学习&#xff08;RL&#xff09;在昇腾NPU上训练比监督学习复杂得多。你需要同时跑策略网络、价值网络&#xff0c;维护动态的经验回放缓冲区&#xff0c;还要处理动态Shape和显存碎片化问题。 这篇将手把手教你如何在昇腾NPU上高效训练RL模型&#xff0c;涵盖PPO/GRPO算法…

作者头像 李华