【数据结构】-- 栈和队列

🐇
🔥博客主页: 云曦
📋系列专栏:数据结构💨吾生也有涯,而知也无涯 💛 感谢大家👍点赞 😋关注📝评论
文章目录
- 前言
- 一、栈
- 📙1.1 栈的概念及结构
- 📙1.2 栈的实现
- 📜1.2.1 栈的结构
- 📜1.2.2 初始化栈
- 📜1.2.3 入栈
- 📜1.2.4 出栈
- 📜1.2.5 获取栈顶元素
- 📜1.2.6 获取栈中有效元素个数
- 📜1.2.7 检测栈是否为空
- 📜1.2.8 销毁栈
- 📜1.2.9 栈所有接口的测试
- 二、队列
- 📙2.1 队列的概念及结构
- 📙2.2 队列的实现
- 📜2.2.1 队列的结构
- 📜2.2.2 初始化队列
- 📜2.2.3 队尾入队列
- 📜2.2.4 检测队列是否为空
- 📜2.2.5 队头出队列
- 📜2.2.6 获取队列头部元素
- 📜2.2.7 获取队列尾部元素
- 📜2.2.8 获取队列中有效元素个数
- 📜2.2.9 销毁队列
- 📜2.2.10 队列所有接口的测试
- 完整代码
- 📙**Stack.h**
- 📙**Stack.c**
- 📙**Queue.h**
- 📙**Queue.c**
- 📙**test.c**
前言
在上期中我们把双向链表学完了,这期我们要开始新的一章 “栈和队列” 的讲解,栈和队列基本都是拿我们之前所学的顺序表或链表来实现,可谓是新瓶装旧酒,有了对顺序表和链表的认识,实现栈和队列并不是难事!
一、栈
📙1.1 栈的概念及结构
概念:栈也是一种特殊的线性表,其次栈只允许在固定一端插入删除,进行数据插入或删除操作的一端叫作栈顶,另一端为栈底。 栈中的数据元素遵守后进先出LIFO的原则。
- 压栈:栈插入数据的操作叫作进栈/入栈/压栈,且入数据是在栈顶。
- 出栈:栈删除数据的操作叫作出栈,出数据也在栈顶。
结构:
注意:栈如果用链表实现的话要考虑找前一个节点的问题,因为栈的插入删除都是在栈顶,所以实现栈时推荐用顺序表来实现,因为顺序表在尾部插入数据的代价比较小。
📙1.2 栈的实现
📜1.2.1 栈的结构
既然用顺序表实现那么栈的结构就是顺序表的结构
typedef int STDataType;typedef struct Stack
{int* arr;int top;//栈顶int capacity;//容量
}ST;
📜1.2.2 初始化栈
因为在栈的结构里,插入数据只能在栈顶插入,所有只需要实现一个插入的接口即可,把malloc改到插入接口(push)里即可.
void STInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->capacity = 0;ps->size = 0;
}
📜1.2.3 入栈
void STPush(ST* ps, STDataType x)
{assert(ps);//检测容量if (ps->top == ps->capacity){//使用三目操作符来解决扩容问题int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType)*newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->arr = tmp;ps->capacity = newcapacity;}//入栈ps->arr[ps->top] = x;ps->top++;
}
需要注意的是:realloc有一个特性,如果传入的参数是NULL,则会像malloc一样开辟一个空间出来,相当于调用了malloc。
📜1.2.4 出栈
出栈很简单让栈顶减1即可。
void STPop(ST* ps)
{assert(ps);//栈为空就不能再删了assert(ps->top > 0);ps->top--;
}
需要注意的是:插入的指针有可能为空或者栈有可能为空,两个情况都要检查一下
📜1.2.5 获取栈顶元素
top是有效数据个数后一个的位置,减1就为栈顶的元素
STDataType STTop(const ST* ps)
{assert(ps);return ps->arr[ps->top-1];
}
📜1.2.6 获取栈中有效元素个数
top就是有效数据的个数,直接返回top即可
int STSize(const ST* ps)
{assert(ps);return ps->top;
}
📜1.2.7 检测栈是否为空
返回top是否等于0,等于返回真,不等于返回假
bool STEmpty(const ST* ps)
{assert(ps);return ps->top == 0;
}
📜1.2.8 销毁栈
注意:这里的销毁指得是把这块空间还给操作系统。
void STDestroy(ST* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = 0;ps->top = 0;
}
📜1.2.9 栈所有接口的测试
void TestStack()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);STPush(&s, 5);printf("栈的有效元素个数%d\n", STSize(&s));while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}//STPop(&s);printf("\n栈的有效元素个数%d\n", STSize(&s));STDestroy(&s);}

当栈为空还在删除时:
二、队列
📙2.1 队列的概念及结构
概念:队列也是一种只允许在一端插入数据,在另外一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO。
- 入队列:进行插入操作的一端称为队尾。
- 出队列:进行删除操作的一端称为队头。
结构:
注意:队列也能用顺序表和链表实现,但使用链表的结构实现更优一些,因为如果是顺序表的话,头删要挪移数据才能完成,效率比链表低,所以实现队列推荐用链表来实现。
📙2.2 队列的实现
📜2.2.1 队列的结构
既然使用队列实现,那么结构自然是链表的形式。
typedef int QueDataType;typedef struct QueueNode
{QueDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;
因为是单链表,出队列是头删,入队列是尾插,导致需要一个头指针和尾指针,且单链表计算长度要遍历一遍数组,这样下来:
- 要传头和尾的参数
- 计算链表长度要遍历链表,复杂度为O(N)
- 不如直接把头尾指针和定义一个存储链表元素个数的变量整合为一个结构体这样就方便多了
📜2.2.2 初始化队列
void QueueInit(Que* pq)
{pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}
只有一个插入的接口,那么就直接在插入接口获取节点就行。
📜2.2.3 队尾入队列
首先获取并初始化节点,然后就是尾插,尾插分为两种情况:
- 链表为空,特殊处理。
- 链表不为空,正常尾插。
void QueuePush(Que* pq, QueDataType x)
{//获取并初始化节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("newnode fail");exit(-1);}newnode->data = x;newnode->next = NULL;//入队列if (pq->phead == NULL){//链表为空pq->phead = newnode;pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}
📜2.2.4 检测队列是否为空
直接返回pq->phead == NULL,等于返回真,不等于返回假
bool QueueEmpty(Que* pq)
{assert(pq);return pq->phead == NULL;
}
📜2.2.5 队头出队列
单链表头删很简单,但要注意:链表为空时,不能再删了,否则会出问题
void QueuePop(Que* pq)
{assert(pq);//检测链表是否为空,为空就不能再删了assert(!QueueEmpty(pq));//出队列QNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;pq->size--;
}
📜2.2.6 获取队列头部元素
phead指向的就是头节点,返回头节点的元素即可,但要注意:链表有可能为空,所以要检测一下
QueDataType QueueFront(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->phead->data;
}
📜2.2.7 获取队列尾部元素
tail指向的就是尾节点,直接返回尾节点的元素即可,但要注意:链表有可能为空,所以要检测一下
QueDataType QueueBack(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->tail->data;
}
📜2.2.8 获取队列中有效元素个数
size存储的就是队列的有效元素个数,返回size即可。
int QueueSize(Que* pq)
{assert(pq);return pq->size;
}
📜2.2.9 销毁队列
- 这里的销毁指的也是把这片空间还给操作系统
- 遍历一遍队列,记录下一个节点的位置,释放掉当前节点,最后将头尾指针置空,size置为0即可。
void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* Next = cur->next;free(cur);cur = Next;}pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}
📜2.2.10 队列所有接口的测试
TestQueue()
{Que obj;QueueInit(&obj);QueuePush(&obj, 1);QueuePush(&obj, 2);QueuePush(&obj, 3);QueuePush(&obj, 4);QueuePush(&obj, 5);printf("队尾元素:%d\n", QueueBack(&obj));printf("队列的有效数据个数:%d\n", QueueSize(&obj));while (!QueueEmpty(&obj)){printf("%d ", QueueFront(&obj));QueuePop(&obj);}//QueuePop(&obj);printf("\n队列的有效数据个数:%d\n", QueueSize(&obj));QueueDestroy(&obj);
}

当队列为空还在删时:
完整代码
*
📙Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//栈
typedef int STDataType;typedef struct Stack
{int* arr;int top;//栈顶int capacity;//容量
}ST;//初始化栈
void STInit(ST* ps);
//入栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//获取栈顶元素
STDataType STTop(const ST* ps);
//获取栈中有效元素的个数
int STSize(const ST* ps);
//检测栈是否为空
bool STEmpty(const ST* ps);
//销毁栈
void STDestroy(ST* ps);
📙Stack.c
#include "Stack.h"void STInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->capacity = 0;ps->top = 0;
}void STPush(ST* ps, STDataType x)
{assert(ps);//检测容量if (ps->top == ps->capacity){//使用三目操作符来解决扩容问题int newcapacity = ps->capacit==0 ? 4 : ps->capacity*2;STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType)*newcapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}ps->arr = tmp;ps->capacity = newcapacity;}//入栈ps->arr[ps->top] = x;ps->top++;
}void STPop(ST* ps)
{assert(ps);//栈为空就不能再删了assert(ps->top > 0);ps->top--;
}STDataType STTop(const ST* ps)
{assert(ps);return ps->arr[ps->top-1];
}int STSize(const ST* ps)
{assert(ps);return ps->top;
}bool STEmpty(const ST* ps)
{assert(ps);return ps->top == 0;
}void STDestroy(ST* ps)
{assert(ps);free(ps->arr);ps->arr = NULL;ps->capacity = 0;ps->top = 0;
}
*
📙Queue.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//队列
typedef int QueDataType;typedef struct QueueNode
{QueDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;//初始化队列
void QueueInit(Que* pq);
//入队列
void QueuePush(Que* pq, QueDataType x);
//检测队列是否为空
bool QueueEmpty(Que* pq);
//出队列
void QueuePop(Que* pq);
//获取队列头部元素
QueDataType QueueFront(Que* pq);
//获取队列尾部元素
QueDataType QueueBack(Que* pq);
//获取队列中有效元素的个数
int QueueSize(Que* pq);
//销毁队列
void QueueDestroy(Que* pq);
📙Queue.c
#include"Queue.h"void QueueInit(Que* pq)
{assert(pq);pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}void QueuePush(Que* pq, QueDataType x)
{assert(pq);//获取并初始化节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("newnode fail");exit(-1);}newnode->data = x;newnode->next = NULL;//入队列if (pq->phead == NULL){//链表为空pq->phead = newnode;pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}bool QueueEmpty(Que* pq)
{assert(pq);return pq->phead == NULL;
}void QueuePop(Que* pq)
{assert(pq);//检测链表是否为空,为空就不能再删了assert(!QueueEmpty(pq));//出队列QNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;pq->size--;
}QueDataType QueueFront(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->phead->data;
}QueDataType QueueBack(Que* pq)
{assert(pq);//检测链表是否为空assert(!QueueEmpty(pq));return pq->tail->data;
}int QueueSize(Que* pq)
{assert(pq);return pq->size;
}void QueueDestroy(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* Next = cur->next;free(cur);cur = Next;}pq->phead = NULL;pq->tail = NULL;pq->size = 0;
}
*
📙test.c
#include"Stack.h"
#include"Queue.h"void TestStack()
{ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);STPush(&s, 5);printf("栈的有效元素个数%d\n", STSize(&s));while (!STEmpty(&s)){printf("%d ", STTop(&s));STPop(&s);}//STPop(&s);printf("\n栈的有效元素个数%d\n", STSize(&s));STDestroy(&s);}TestQueue()
{Que obj;QueueInit(&obj);QueuePush(&obj, 1);QueuePush(&obj, 2);QueuePush(&obj, 3);QueuePush(&obj, 4);QueuePush(&obj, 5);printf("队尾元素:%d\n", QueueBack(&obj));printf("队列的有效数据个数:%d\n", QueueSize(&obj));while (!QueueEmpty(&obj)){printf("%d ", QueueFront(&obj));QueuePop(&obj);}//QueuePop(&obj);printf("\n队列的有效数据个数:%d\n", QueueSize(&obj));QueueDestroy(&obj);
}int main()
{TestStack();TestQueue();return 0;
}
相关文章:
【数据结构】-- 栈和队列
🐇 🔥博客主页: 云曦 📋系列专栏:数据结构 💨吾生也有涯,而知也无涯 💛 感谢大家👍点赞 😋关注📝评论 文章目录 前言一、栈📙1.1 栈…...
使用SpringAop切面编程通过Spel表达式实现Controller权限控制
目录 参考一、概念SpEL表达式 二、开发引入包定义注解定义切面定义用户上下文 三、测试新建Service在方法上注解新建Service在类上注解运行 参考 SpringBoot:SpEL让复杂权限控制变得很简单 一、概念 对于在Springboot中,利用自定义注解切面来实现接口…...
Flutter:简单搞一个内容高亮
内容高亮并不陌生,特别是在搜索内容页面,可以说四处可见,就拿掘金这个应用而言,针对某一个关键字,我们搜索之后,与关键字相同的内容,则会高亮展示,如下图所示: 如上的效果…...
2023/08/10
文章目录 一、计算属性传参二、小程序、h5跳转其他平台授权三、封装popup弹窗四、实现保存海报五、下载图片和复制分享链接 一、计算属性传参 计算属性的值往往通过一个回调函数返回,但是这个回调函数是无法传递参数的,要想实现计算属性传参可以通过闭包…...
LeetCode 1289. 下降路径最小和 II:通俗易懂地讲解O(n^2) + O(1)的做法
【LetMeFly】1289.下降路径最小和 II:通俗易懂地讲解O(n^2) O(1)的做法 力扣题目链接:https://leetcode.cn/problems/minimum-falling-path-sum-ii/ 给你一个 n x n 整数矩阵 arr ,请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下…...
Coin Change
一、题目 Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent, and 1-cent. We want to make changes with these coins for a given amount of money. For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-c…...
2023 8 -14链表OJ
💕人面只今何处去,桃花依旧笑春风💕 作者:Mylvzi 文章主要内容:详解链表OJ题 题目一:环形链表(判断链表是否带环) 题目描述: 画图分析: 代码实现&#x…...
大数据必回之LSM树
LSM树(Log-Structured-Merge-Tree)并不像B、红黑树一样是一颗严格的树状数据结构,它其实是一种存储结构,像HBase、RocksDB这些NoSQL存储都是采用LSM树。它是一种分层、有序、面向磁盘的数据结构,核心思想是顺序写性能远…...
Vue中的Object.defineProperty详解
Vue中的Object.defineProperty是一个比较重要的方法,它是可以定义对象中属性的一个方法,相比于在对象中直接定义的对象,它更具有灵活性。 直接定义对象中的属性是这样的: let person {name:张三,address:广东,age:12,} 而Object.…...
MySQL高阶知识点(一)一条SQL【更新】语句是如何执行的
一条SQL【更新】语句是如何执行的 首先,可以确定的说,【查询】语句的那一套流程,【更新】语句也是同样会走一遍,与查询流程不一样的是, 更新语句涉及到【事务】,就必须保证事务的四大特性:ACID&…...
threejs实现模型gltf的动画效果
确保加载模型后模型有animations属性。加载完模型后,在模型中定义mixer的变量值。 // 4、加入加载器 const loader new GLTFLoader(); loader.load("./model/gltf/RobotExpressive/RobotExpressive.glb", function (gltf) {// 赋值动画给mixermixer ne…...
Harmony创建项目ohpm报错
Harmony创建FA模型的项目时报如下错: The registry is empty - edit .ohpmrc file or use "ohpm config set registry your_registry" command to set registry.解决方法: File -> Settings -> Build,Execution,Deployment -> Ohpm …...
44 | 酒店预订及取消的数据分析
1.背景介绍 数据集来自Kaggle网站上公开的Hotel booking demand项目 该数据集包含了一家城市酒店和一家度假酒店的预订信息,包括预订时间、入住时间、成人、儿童或婴儿数量、可用停车位数量等信息。 数据集容量约为12万32 本次数据分析主要包含如下内容: 总览数据,完成对…...
物联网和不断发展的ITSM
物联网将改变社会,整个技术行业关于对机器连接都通过嵌入式传感器、软件和收集和交换数据的电子设备每天都在更新中。Gartner 预测,全球将有4亿台互联设备投入使用。 无论企业采用物联网的速度如何,连接设备都将成为新常态,IT服务…...
加了ComponentScan,但是feign接口无法注入的原因
正文 正确的注入 如果发现无法注入:看看启动类Application是否有加入注解:EnableFeignClients(AppConstant.BASE_PACKAGES) 注意:EnableFeignClients和ComponentScan是两个独立的扫描,所以,如果只配置了ComponentSca…...
C#Winform中DataGridView控件显示行号实例
本文演示C#Winform中如何给DataGridView控件显示行号。 首先创建winform项目,添加DataGridView控件,给控件添加两列。 修改CS代码: using System.Windows.Forms;namespace DataGridviewDemo {public partial class Form1 : Form{public Form1(){InitializeComponent();//添…...
Stable Diffusion WebUI安装和使用教程(Windows)
目录 下载Stable Diffusion WebUI运行安装程序,双击webui.bat界面启动插件安装(github)模型下载(有些需要魔法)安装过程遇到的大坑总结参考的博客 整个过程坑巨多,我花了一个晚上的时间才全部搞定,本教程针对有编程基础…...
LeetCode 35题:搜索插入位置
题目 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2:…...
Linux系统中常见的几种软件包管理器
软件包管理器 DPKGAPT(APT-GET)RPMYUMDNF Linux软件包管理工具是一组命令的集合,其作用是在操作系统中提供安装、更新、删除及卸载软件的方法,同时提供对系统中所有软件状态信息的查询。不同的Linux发行版会有不同的包管理器&…...
python异步IO完全指南
原地址:https://flyingbyte.cc/post/async_io/ python异步IO完全指南 做为一种并行编程的範式,异步IO在Python中非常受重视,从Python3.4到3.7快速演进。 我们已经有多线程,多进程,并发(concurrency&#x…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...



