【数据结构】栈和队列的定义与实现

【数据结构】栈和队列的定义与实现

主页:HABUO🍁主页:HABUO

🍁如果再也不能见到你,祝你早安,午安,晚安🍁


1.栈

1.1 栈的定义及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。 如下图所示:

总结:其实栈就是一个后进先出的一个容器,注意可以边进边出,什么意思呢?例如上述图,a1进栈之后可以出栈之后a2再进栈,因此一个入栈序列可以对应多个出栈序列。 

1.2 栈的实现

分析:我们前面学过了顺序表也即是可以动态增容的数组,也学过了链表,无论是带头不带头、循环不循环、双向不双向,我们是不是都搞定了。那我们思考一个问题,实现栈用哪种结构比较好呢?无论顺序表,链表我们是不是都实现过它们的增删查改,因此它们实现栈都是可以的,不过用顺序表更方便一些,因为你想嘛,我们只需要在一端进行操作,那现成有数组的形式在这放着,我们为什么去选择更复杂的结构呢😅,所以我们选择顺序表来实现栈。

大体思想见下图:

其实顺序表我们前面早已实现,用其实现栈也无非注意一些细节而已,不再详细赘述。

typedef int StackDataType; typedef struct Stack { StackDataType* _Array; int _top; int _capacity; }Stack; //栈初始化 void StackInit(Stack* s); //销毁栈 void StackDestory(Stack* s); //入栈 void StackPush(Stack* s, StackDataType x); //出栈 void StackPop(Stack* s); //获取栈顶元素 StackDataType StackTop(Stack* s); //获取栈有效元素个数 int StackSize(Stack* s); //判断栈是否为空,空返回1,非空返回0 int StackEmpty(Stack* s);

这是整个工程文件的头文件,放置栈的各种结构和接口的声明,我们主要就是进行出栈入栈操作,因此相对于顺序表而言,增删查改的接口相对也就少了,下面对其进行一一实现。

//栈初始化 void StackInit(Stack* s) { assert(s); s->_Array = (Stack*)malloc(sizeof(Stack) * ARRAYINITSIZE); s->_top = 0; s->_capacity = ARRAYINITSIZE; } //销毁栈 void StackDestory(Stack* s) { assert(s); free(s->_Array); s->_Array = NULL; s->_top = s->_capacity = 0; }

这里需要注意的就是栈初始化的地方,我们可以将指针置为NULL但是,在入栈的时候我们就麻烦了需要进行情况的判断,因此我们就在初始化的时候直接开辟出一段空间来,省的到后面再进行判断,到入栈操作时我们只需判断栈满不满即可,满了就增容,不满就直接插入数据。

//入栈 void StackPush(Stack* s, StackDataType x) { assert(s); if (s->_top == s->_capacity) { s->_capacity *= 2; Stack* temp = (Stack*)realloc(s->_Array, sizeof(Stack) * s->_capacity); if (temp == NULL) { printf("内存不足\n"); exit(-1); } else { s->_Array = temp; } } s->_Array[s->_top] = x; s->_top++; } //出栈 void StackPop(Stack* s) { assert(s); assert(s->_top != 0); s->_top--; }

入栈操作相对于前面顺序表的增删查改的操作,这里就太简单了,不赘述,有需要了解的翻看前面博客顺序表的实现即可。这里需要注意assert(s->top != 0),我们在这里加一个断言是防止栈里面没有元素,我们还进行出栈操作。

其它的接口只需返回相应的变量即可,这里我们会不会有个问题?既然直接返回相应变量,那我们还为什么去建立一个接口,其实这是为了保持我们接口的一致性,直接调用即可。 

2.队列

2.1 队列的定义及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out),结构如下图所示:

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头 

总结:队列与我们日常排队的情形大同小异,谁先进谁就先出嘛,绝对不存在后进先出的情况,因此呢,也就意味着一种入队序列只能是对应一种出队序列。

2.2 队列的实现

分析:和上面栈的实现一样,我们采取哪种结构来进行实现呢?因为这里需要一头进,另一头出,只要是这种两头操作的我们顺序表是不是不太好,因为你只要头进行操作了,顺序表后面的数据是不是需要移动,那这里效率就下来了,所以顺序表不太好,那链表用那种形式呢?其实单链表是不完全就足够这里的功能了,为什么呢?因为我们就仅仅需要个头删或者尾插,这对单链表来说是最基本的功能了,那我们能用单链表的就尽量不要其它链表的形式,因为毕竟没必要,何必再去多存地址。综上,我们采用单链表的形式来实现队列。

typedef int QueueDataType; typedef struct QueueListNode { QueueDataType _data; struct QueueListNode* _next; }QueueListNode; typedef struct Queue { QueueListNode* _front; QueueListNode* _rear; }Queue; // 初始化队列 void QueueInit(Queue* q); // 队尾入队列 void QueuePush(Queue* q, QueueDataType data); // 队头出队列 void QueuePop(Queue* q); // 获取队列头部元素 QueueDataType QueueFront(Queue* q); // 获取队列队尾元素 QueueDataType QueueBack(Queue* q); // 获取队列中有效元素个数 int QueueSize(Queue* q); // 检测队列是否为空,如果为空返回非零结果,如果非空返回0 int QueueEmpty(Queue* q); // 销毁队列 void QueueDestroy(Queue* q);

这是整个工程文件的头文件,放置队列的各种结构和接口的声明,其实和栈的接口的实现基本差不多,这里唯一需要注意的是我们通过了一个结构体存放了连个头尾指针,为什么要这样做?目的就是在于使我们接口为了保持一致性,如果不这样做有部分接口需要传二级指针,那这里我们传过去结构体的地址是不是就能对结构体里的变量进行操作,换言之这些指针是不是就能改变了,而不仅仅是那些形参进行改变,我们主要就是进行入队列和出队列操作,因此相对于单链表而言,增删查改的接口相对也就少了,下面对其进行一一实现。 

// 队尾入队列 void QueuePush(Queue* q, QueueDataType data) { assert(q); if (q->_front == NULL) { QueueDataType* temp = (QueueListNode*)malloc(sizeof(QueueListNode)); if (temp == NULL) { printf("内存不足\n"); exit(-1); } else { q->_front = q->_rear = temp; } } else { QueueListNode* temp = (QueueListNode*)malloc(sizeof(QueueListNode)); if (temp == NULL) { printf("内存不足\n"); exit(-1); } else { q->_rear->_next = temp; q->_rear = temp; } } q->_rear->_data = data; q->_rear->_next = NULL; } // 队头出队列 void QueuePop(Queue* q) { assert(q); assert(q->_front != NULL); QueueListNode* frontNext = q->_front->_next; free(q->_front); q->_front = frontNext; }

你看这里的入队列就分情况了,因为这和顺序表不一样,如果我们在初始化的时候没有直接进行开辟空间,为什么?因为你都不知道要放什么数据,即使你建立节点,到入队列时还是要分情况,一种是第一次入队列,你只需要将你初始化的节点的val改成所需要的数据,如果要新建立节点,你就需要再malloc一个节点。因此无论怎么样都需要分两种情况,顺序表就不需要这样,顺序表我们可以通过下标进行访问嘛。

其它的接口大同小异都是返回各种变量的数据即可,不再赘述。


总结:这篇博客我们认识两个新的数据结构,我们会不会有个这样的问题?哪里可以用到这些数据结构呢?对于栈,其实用处还蛮大的,比如如果我们想设计一个迷宫游戏,那这个走到死路时我们是不是要后退啊,这就涉及一个后进先出的问题,就可以利用我们的栈来进行实现。对于队列,日常生活中还是很常见的,我们都去过银行或者什么办公场所,是不是都有一个排队报号机,这就是一个非常典型的队列问题,先来的先排队,到了就出队列。因此今天我们所学的两种数据结构,肯定会对我们有所启发,希望我们都能有所收获。💯


🏋不是每一粒种子都能开花,但播下种子就比荒芜的旷野强百倍🏋


我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:腾讯云自媒体同步曝光计划 - 腾讯云开发者社区-腾讯云 

Read more

从安装到实测:基于 Claude Code + GLM-4.7 的前端生成与评测实战

从安装到实测:基于 Claude Code + GLM-4.7 的前端生成与评测实战

目录 引言 一、命令行使用 Claude Code(安装与配置) 步骤一:安装 Claude Code(命令行) 步骤二:配置蓝耘MaaS平台 步骤三:常见排查 二、编码工具中使用 claude-code:三个端到端案例(含提示与实测评价) 案例 1:交互式个人血压记录网页 — 前端端到端生成 案例 2:Web 双人对战小游戏(Joy-Con 风格) 案例 3:前端可视化组件生成 三、补充建议(快速 checklist) 总结 引言 近一年来,代码生成类工具逐渐从“写几行示例代码”走向“完整功能交付”,但真正落到工程实践时,很多工具仍停留在 Demo 阶段:要么跑不起来,

By Ne0inhk

图论基础与遍历算法(BFS+DFS)

一、图的核心概念 1. 图的定义:图 G=(V,E) 由顶点(节点 V)和边(E)组成,是描述元素间关联关系的核心数据结构。 2. 图的分类:无向图(边无方向)、有向图(边有方向,从起点指向终点)、带权图(边附带距离、概率等权重信息)。 二、图的存储方式 (一)邻接矩阵 * 结构:n 个顶点的图对应 n×n 矩阵,通过矩阵元素值表示顶点间连接关系。 * 关键特性:无向图的邻接矩阵是对称矩阵(a[i][j]=a[j][i],1 表示有边,0 表示无边);有向图的邻接矩阵不一定对称(a[

By Ne0inhk
【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

🔥 本文专栏:c++ 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录:努力不是为了回报,而是不让自己留下任何遗憾 ★★★ 本文前置知识: map和set模拟实现 引入 那么在正式讲解STL的unordered_map以及unordered_set这两个容器之前,我们先来回顾一下,目前我们接触到能够高效查找数据的数据结构,那么首先我们可以想到的能够实现高效查找数据的数据结构便是数组,但是这里的数组不是简单的将元素直接存放到数组中的任意位置,而是会将存储在数组中的元素先进行一次排序,然后借助二分算法来进行查找,由于这里数组的排序只需要一次,那么排序付出的代价可以均摊到每一次的查找操作中,所以这里排序的代价可以忽略不计,而二分查找的时间复杂度则是logN,所以这种方式能够实现高效的数据查找,但是如果涉及到插入以及删除操作的话,如果插入以及删除元素不在数组末尾,那么必然就要移动大量的元素,意味着插入和删除的时间复杂度最坏情况下会到达O(N),效率相比于查找就不那么高效 接着就是在二叉搜索树的基础上优化,压缩其高度的AVL树和红黑树这两个数据结构,这两种数据结

By Ne0inhk
【排序算法全家桶 Level 3】交换排序:从冒泡优化到快排四重奏

【排序算法全家桶 Level 3】交换排序:从冒泡优化到快排四重奏

🏠 个人主页:EXtreme35 📚 个人专栏: 专栏名称专栏主题简述《C语言》C语言基础、语法解析与实战应用《数据结构》线性表、树、图等核心数据结构详解《题解思维》算法思路、解题技巧与高效编程实践 目录 * 一、 冒泡排序 * 1.1 算法思想:气泡升腾的奥秘 * 1.2 为什么你的冒泡排序总是比别人慢? * 1.3 代码实现 * 二、快速排序 * 2.1 初始版本:Hoare 版 * 2.1.1 初始代码 * 2.1.2 优化一:三数取中 * 2.1.2 优化二:小区间优化 * 2.2

By Ne0inhk