【数据结构(C语言)】浅谈栈和队列

目录

文章目录

前言

一、栈

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 销毁栈

二、队列

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 销毁队列

总结


一、栈

1.1 栈的概念及结构

栈(Stack)是一种线性数据结构,它可以看作是一种特殊的列表。栈只能在一端进行插入和删除操作,这一端被称为栈顶(Top)。栈按照后进先出(LIFO, Last In First Out)的原则进行操作,即最后一个进栈的元素最先被弹出。栈可以用数组或链表实现。栈的主要操作包括压栈(进栈/入栈)(Push)和弹栈(出栈)(Pop),还有取栈顶元素(Top)和判断栈是否为空(Empty)等。栈在编程中常用于函数调用、表达式求值、括号匹配等场景。

1.2 栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。所以我们这里使用数组实现栈。用数组实现也分为静态的和动态的,静态的实际中一般不实用,所以我们实现动态的

1.2.1. 支持动态增长的栈的结构

这里我们声明一个结构体,一个成员为指针a,用来存放开辟空间的首地址(即动态数组),一个成员top用来存放栈顶位置,还有一个成员capacity用来存放开辟的空间大小,方便数组扩容。

代码如下:

typedef int StDataType;typedef struct Stack
{StDataType* a;int top;		// 标识栈顶位置的int capacity;
}St;

1.2.2 初始化栈

将结构体中的指针a赋值为NULL,将capacity赋值为0,将top赋值为0,表示top指向栈顶元素的下一个位置(也可以赋值为-1,表示top指向栈顶元素,这里我们使用第一种)。

代码如下:

void StInit(St* pst)
{assert(pst);pst->a = NULL;pst->capacity = 0;//表示top指向栈顶元素的下一个位置pst->top = 0;//表示top指向栈顶元素,如果为-1,后面的代码也要改//pst->top = -1;
}

1.2.3 入栈

入栈之前要先判断开辟的空间满了没,如果满了,可以使用realloc()函数重新分配数组大小,然后再将元素插入top所指向的位置,最后将top+1。

代码如下:

void StPush(St* pst, StDataType x)
{assert(pst);if (pst->top == pst->capacity){int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;StDataType* tmp = (StDataType*)realloc(pst->a, newCapacity * sizeof(StDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}pst->a = tmp;pst->capacity = newCapacity;}pst->a[pst->top] = x;pst->top++;
}

1.2.4 出栈

先要确保栈中有元素,可以使用断言,如果有,则只需要top-1就行。

代码如下:

void StPop(St* pst)
{assert(pst);assert(pst->top > 0);pst->top--;
}

1.2.5 获取栈顶元素

先确保栈中有元素,然后直接返回top-1所指向位置的元素即可。

代码如下:

StDataType StTop(St* pst)
{assert(pst);assert(pst->top > 0);return pst->a[pst->top - 1];
}

1.2.6 获取栈中有效元素个数

因为top指向栈顶元素的下一个位置,其大小刚好为元素的个数,所以直接返回top即可。

代码如下:

int StSize(St* pst)
{assert(pst);return pst->top;
}

1.2.7 检查栈是否为空

判断top是否为0,为0即为空。

代码如下:

bool StEmpty(St* pst)
{assert(pst);return pst->top == 0;
}

1.2.8 销毁栈

先将动态分配的内存释放了,再将结构体中的成员赋值为初始化栈时所赋值的值就行。

代码如下:

void StDestroy(St* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->top = 0;pst->capacity = 0;
}

二、队列

2.1 队列的概念及结构

队列(Queue)是一种遵循先进先出(First In First Out,FIFO)原则的数据结构,即最先插入队列的元素最先被取出。队列具有两个端点,一个是队头(Head),一个是队尾(Tail),入队操作(enqueue)在队尾进行,出队操作(dequeue)在队头进行。队列的应用领域很广,例如实现任务调度、消息传递、缓冲区等。常见队列的实现包括:单向队列、双向队列和循环队列等。我们这里主要讨论单向队列。

2.2 队列的实现

我们这里实现的是最基础的单向队列,双向队列和循环队列各位读者可另行了解。队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低(因为要整体把元素往前移,时间复杂度为O(n),虽然在算法中用数组模拟实现队列可以使用头尾双指针使时间复杂度变成O(1),但这样做出队的同时也把空间浪费了)。

2.2.1 队列的结构

因为使用链表实现队列,所以先声明一个队列的结点的结构体,其成员跟链表的声明一致,有两个成员,一个为数据val,另一个为next指针。然后为了后面实现的方便,再声明一个队列结构体,其成员包括队头指针phead和队尾指针ptail以及一个记录队列大小的整形size。

代码如下:

typedef int QDataType;typedef struct QueueNode
{QDataType val;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;

2.2.2 初始化队列

将队头指针phead和队尾指针ptail赋值为NULL,将size赋值为0。

代码如下:

void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}

2.2.3 入队

先动态申请一个新结点,将要入队的元素赋给新结点的val,再将新结点的next指针指向NULL。然后判断这个队列是否为空,如果为空,则将队头指针phead和队尾指针ptail都指向新结点;如果不为空,则只要改变队尾指针ptail就行,即先将队尾指针ptail指向的结点的next指针指向新结点,再将队尾指针ptail指向新结点。最后将size+1就行了。

代码如下:

void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newNode = (QNode*)malloc(sizeof(QNode));if (newNode == NULL){perror("malloc fail");exit(-1);}newNode->val = x;newNode->next = NULL;if (pq->phead == NULL){pq->phead = pq->ptail = newNode;}else{pq->ptail->next = newNode;pq->ptail = newNode;}pq->size++;
}

2.2.4 出队

先要确保队列中有结点,可以使用断言。然后再判断队列中是否只有一个结点,如果是,则释放这个结点后,要将队头指针phead和队尾指针ptail都指向NULL;如果不是,则只需要将队头指针phead指向它所指向的结点的下一个结点,并将它原来所指向的结点释放就行。最后将size-1。

代码如下:

void QueuePop(Queue* pq)
{assert(pq);assert(pq->phead);if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QNode* tmp = pq->phead;pq->phead = pq->phead->next;free(tmp);}pq->size--;
}

2.2.5 获取队头元素

先确保队列有结点,再返回队头指针phead所指向的结点的值val。

代码如下:

QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->phead);return pq->phead->val;
}

2.2.6 获取队尾元素

先确保队列有结点,再返回队尾指针ptail所指向的结点的值val。

代码如下:

QDataType QueueBack(Queue* pq)
{assert(pq);assert(pq->ptail);return pq->ptail->val;
}

2.2.7 获取队列中有效元素个数

直接返回size。

代码如下:

int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

2.2.8 检查队列是否为空

判断队头指针phead是否为NULL。

代码如下:

bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}

2.2.9 销毁队列

先遍历释放队列中的每一个结点,再将队列结构体中的成员赋值为初始化队列时所赋值的值就行。

代码如下:

void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}


总结

以上就是关于栈和队列的基本概念和操作。通过这篇文章,希望大家能够更好地理解关于栈和队列的原理和实现,并在实际编程中灵活运用它们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165245.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javaweb之前后台分离开发介绍的详细解析

2.1 前后台分离开发介绍 在之前的课程中,我们介绍过,前端开发有2种方式:前后台混合开发和前后台分离开发。 前后台混合开发,顾名思义就是前台后台代码混在一起开发,如下图所示: 这种开发模式有如下缺点&a…

使用vcpkg安装库失败的解决方法

1、前言 vcpk是是一款开源的c/c库管理工具,尤其是在windows平台,可以帮助我们很好的管理各种依赖包。 在windows环境做c/c开发的人应该都深有体会,有时候编译需要下载一堆依赖库,导致搭建编译环境特别麻烦。但是,通过v…

前端 vue 面试题(二)

文章目录 如何让vue页面重新渲染组件间通信vue为什么要mutation、 action操作插槽、具名插槽、作用域插槽vue编译使用的是什么库?vue怎么实现treeshakingwebpack实现treeshaking为什么只有es module 能支持 tree shaking mixin 的作用mixin的底层原理nexTick原理vue…

预处理机制

跟着肯哥(不是我)学预处理机制 预处理类别 宏定义:#define 将文本替换为表达式或语句 条件编译:#ifdef、#ifndef和#if、#elif、#endif 根据标识符是否被定义选择编译代码 头文件包含:#include 将其他文件&#x…

Jmeter怎么实现接口关联?

用于接口测试时,后一个接口经常需要用到前一次接口返回的结果,应该如何获取前一次请求的结果值,应用于后一个接口呢,拿一个登录的例子来说明如何获取。 1、打开jmeter,新建一个测试计划,在测试计划里新建一…

将所有图片居中对齐

Ctrl h 调出替换框 ^g表示所有图片 格式里面选择段落 全部替换

winlogbeat采集windows日志

下载链接 https://www.elastic.co/cn/downloads/past-releases/winlogbeat-7-16-2 配置文件 # ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch:# Array of hosts to connect to.hosts: ["192.168.227.160:9200&…

Vue3中如何响应式解构 props

目录 1,前言2,解决2.1,利用插件,实现编译时转换2.2,toRef 和 toRefs 1,前言 Vue3 中为了保持响应性,始终需要以 props.x 的方式访问这些 prop。这意味着不能够解构 defineProps 的返回值&#…

Navicat 技术指引 | 适用于 GaussDB 的数据迁移工具

Navicat Premium(16.2.8 Windows版或以上) 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能,还提供强大的高阶功能(如模型、结构同步、协同合作、数据迁移等),这…

Cesium 展示——地球以及渲染数据导出(下载)为图片或 pdf

文章目录 需求分析新加需求分析第一种方式第二种方式需求 将 Cesium 球体以及渲染数据导出为 jpg/png/pdf 分析 获取场景 scene 信息,转为image 的 octet-stream 流 进行下载为图片 /*** @todo canvas 导出图片* @param {string} dataurl - 地址* @return {Blob}*/ functio…

设备健康管理平台助力锂电企业实现可持续发展

随着锂电池产业的快速发展,设备的稳定运行和精准维护对于锂电企业来说至关重要。传统的设备维护方式在效率和全面性方面存在局限,无法满足锂电行业对设备管理的需求。然而,通过设备健康管理平台的引入,锂电企业现在可以充分发挥其…

【LeetCode:1410. HTML 实体解析器 | 模拟+哈希表+字符串+库函数】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

【C语言】中,输入一个数组,实现将输入的n个数字按照从大到小的顺序输出。【通俗简单易懂】

本篇文章中,我们将讲述在C语言中,输入一个数组,如何用for循环实现将输入的n个数字按照从大到小输出。 一.定义数组并初始化 首先,我们定义一个整形的数组并将其初始化。输入n,来决定数组中整数的个数。 然后用for循…

通过HTML网页对mysql数据库进行增删改查(CRUD实例)

首先我们得了解一下大致的架构 ,如下: 我们采用自底向上的方式进行开发, 一、先写mysql数据库 二、再写java后端(Spring MVC架构)(这个是什么东西不懂不要紧,跟着步骤做就行了) 三、最后写前端页面(HTML) 一、 Mysql数据库部分 我们要通过网页对数据库进行开发,…

解决:Gitee + PicGo配置图床失败

解决:Gitee PicGo配置图床失败 PicGo安装插件的时候选择:gitee-uploader,不要选择gitee! 在Gitee新建的图床仓库中设置一个images文件夹,用来保存上传的图片,但是要注意在PicGo中的path中要写上路径/img…

数据库基础入门 — SQL运算符

我是南城余!阿里云开发者平台专家博士证书获得者! 欢迎关注我的博客!一同成长! 一名从事运维开发的worker,记录分享学习。 专注于AI,运维开发,windows Linux 系统领域的分享! 本…

linux的基础命令

文章目录 linux的基础命令一、linux的目录结构(一)Linux路径的描述方式 二、Linux命令入门(一)Linux命令基础格式 三、ls命令(一)HOME目录和工作目录(二)ls命令的参数1.ls命令的-a选…

基于yolov2深度学习网络的喝水行为检测系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、YOLOv2网络原理 4.2、基于YOLOv2的喝水行为检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 clc; clear; close all; warning off;…

PPT思维导图怎么做?这2个思维导图工具墙裂推荐!

在日常学习和工作中,我们常常会面临需要处理大量信息的情况,这时候,一种叫做思维导图的工具可能会成为你的救星。 不同于传统的线性记录方式,思维导图以其独特的视觉表现力和结构化的信息处理方式,使得人们能够更加有…

Flutter学习(四)如何取消listview的越界效果

背景 在flutter的开发过程中,ListView是很常见的一个组件,但是,由于ListView的某些自带的体验,导致不太好的用户体验。例如ListView中,滑动到顶部或者底部的时候,再次滑动,会有越界的效果&…