栈和队列的实现(详解+图解!文末附完整代码)

news2024/11/18 14:36:26

栈的基本概念

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

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

出栈和入栈的操作如下:
他们都只能从栈顶进行出栈和进栈操作,所以栈要遵守先进后出的原则
在这里插入图片描述
栈的先进先出原则如下:
在这里插入图片描述

栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。所以我们在这里选择用数组来实现栈。
数组栈的结构图:
在这里插入图片描述
下面我们就用数组来是具体实现栈的增删查改等操作
头文件如下:

#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include<stdlib.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack;
// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);
创建栈

首先我们在这里将int作为本次栈的数据类型
我们还要定义一个结构体里面有数组,栈顶元素的下标的+1和栈的容量(栈里面的最多元素个数)

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 
	int capacity;  //  
}Stack;
栈的初始化

栈的初始化就比较简单了,直接将top和容量置为0,并且把数组置为空

void StackInit(Stack* ps)
{
	assert(ps);
	ps->top = 0;
	ps->capacity = 0;
	ps->a = NULL;
}
栈的销毁

栈的销毁同样很简单,将数组free掉,防止出现野指针,同时置空,然后容量和栈顶元素下标置为0

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}
判断栈是否为空

在后面的增删查改等操作我们需要断言,为了增加代码的可读性,我们直接写一个函数 ,更加容易理解,如果栈顶元素下标为0,那么这个栈就为空 ,所以直接return top等于 0这个表达式的值,为空则返回 1,不为空则返回0

int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}
数据进栈

数据进栈是从栈顶入,我们首先要判断这个 栈是否已满,如果满了的话那么栈顶的下标就和容量相等,对于top和capacity的不理解的可以看这个图,top的意思如下图:
在这里插入图片描述
当capacity和top相等时其实是有两种情况的,一种时为空,另一种是栈满,,我们在这里就用一个三目操作符,如果为0,就将capacity初始化为4,否则将capacity的原来的值乘2,然后动态开辟realloc一块新的空间,大小就是newcapacity,这样我们就完成了扩容
然后我们将新空间temp给数组a
最后直接把插入的数据data给a[top],同时top++
在这里插入图片描述

void StackPush(Stack* ps, STDataType data)
{
	if (ps->top == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* temp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (temp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->capacity = newcapacity;
		ps->a = temp;
	}
	ps->a[ps->top] = data;
	ps->top++;
}
数据出栈

出栈及其简单,直接先判断栈是否为空,然后将top–即可

void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}
获取栈顶元素

栈顶元素的下标其实就是top-1,所以直接返回a[top-1]

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}
获取栈内的元素个数

栈内元素个数就是top的值,栈为空top为0

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

栈的实现就完成了,完整代码如下:

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// ջ
	int capacity;  //  
}Stack;

void StackInit(Stack* ps)
{
	assert(ps);
	ps->top = 0;
	ps->capacity = 0;
	ps->a = NULL;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

void StackPush(Stack* ps, STDataType data)
{
	if (ps->top == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* temp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (temp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->capacity = newcapacity;
		ps->a = temp;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

队列

栈的基本概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

在这里插入图片描述

队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低
这里我们就用列表的结构来实现队列
头文件如下:
注意,这里我们定义了两个结构体,,第一个结构体其实只是链表的结构,里面有一个next一个data,第二个则是队列的结构,一个队头(出数据)指针,一个队尾指针(进数据),还有一个队列的大小

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<ctype.h>

// 链式结构:表示队列 
typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QDataType size;
	QNode* front;
	QNode* rear;
}Queue;

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

初始目前以及是熟能生巧了,很简单,size置为0,头尾指针置为空即可

void QueueInit(Queue* q)
{
	assert(q);
	q->size = 0;
	q->front = q->rear = NULL;
}
队列的销毁

队列的销毁我们需要花点功夫理解一下:
因为队列有多个节点,全部都要free掉 ,不然就可能会出现野指针的问题,所以我们在这里用一个while循环,将cur置为头指针,将next置为cur的next,这样当前cur的next就不会丢失。首先我们把cur给free掉,然后将cur置为next继续操作,直到cur为空,然后将front和rear置为空,size置为0即可。
在这里插入图片描述

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}
判断队列是否为空

和栈一样,提高代码的可读性,如果size=0,其实队列就为空,但是头尾指针都为空时也可以,为空时返回1,不为空时返回0

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == NULL && q->rear == NULL;
	/*return q->size == 0;*/
}
队列数据插入

要插入一个数据我们首先要做的就是开辟有一个新的链表节点,然后将他的next置为空,data置为你要插入的数据
插入数据分为两种情况,链表为空和不为空
链表为空时直接将front和rear置为新节点newnode同时size++
链表不为空时就将front的next置为newnode,rear置为newnode同时size++

void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	newnode->data = data;
	newnode->next = NULL;
	if (q->rear == NULL)
	{
		assert(q->front == NULL);
		q->front = q->rear = newnode;
	}
	else
	{
		q->rear->next = newnode;
		q->rear = newnode;
	}
	q->size++;
}
队列数据删除

队列的删除我们就要判断队列是否为空,为空就不能删除了
同样的有两种情况:
链表只有一个节点时直接将头尾指针都置为空即可,size–
链表有多个节点时就将front的next置为front,size–
在这里插入图片描述

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	if (q->rear == q->front)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	else
	{
		QNode* next = q->front->next;
		free(q->front);
		q->front = next;
	}
	q->size--;
}
获取队头数据

获取队头的元素 ,直接返回头指针的data即可

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}
获取队尾数据

获取队尾元素 同样简单,直接返回rear的data即可

QDataType QueueBack(Queue* q)
{
	assert(q);
	return q->rear->data;
}
求队列元素个数

直接返回size的值即可

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

队列的实现就完成了
完整代码如下:

void QueueInit(Queue* q)
{
	assert(q);
	q->size = 0;
	q->front = q->rear = NULL;
}

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == NULL && q->rear == NULL;
	/*return q->size == 0;*/
}

void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}
	newnode->data = data;
	newnode->next = NULL;
	if (q->rear == NULL)
	{
		assert(q->front == NULL);
		q->front = q->rear = newnode;
	}
	else
	{
		q->rear->next = newnode;
		q->rear = newnode;
	}
	q->size++;
}

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	//һڵʱ
	if (q->rear == q->front)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	//ڵ
	else
	{
		QNode* next = q->front->next;
		free(q->front);
		q->front = next;
	}
	q->size--;
}

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}

QDataType QueueBack(Queue* q)
{
	assert(q);
	return q->rear->data;
}

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

好了,今天的分享到这里就结束了,谢谢大家的支持!

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

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

相关文章

【Mybatis】基础增删改查

一.创建SpringBoot项目 创建新项目需要添加的依赖 当然如果是以前的项目也可以直接在pom.xml文件中添加依赖: MySQL Driver依赖 <dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</…

如何通过 Al 的能力提升编程的效率?

通过人工智能&#xff08;AI&#xff09;的技术&#xff0c;可以提升编程效率和能力。以下是一些建议和方法&#xff1a; 代码自动生成&#xff1a;使用AI技术&#xff0c;可以根据程序员的需求和输入&#xff0c;自动生成代码。这可以提高编程效率&#xff0c;减少编写代码所需…

Fedora 36 ARM 镜像源更换与软件安装

1、什么是Fedora Fedora Linux是较具知名度的Linux发行套件之一&#xff0c;由Fedora专案社群开发、红帽公司赞助&#xff0c;目标是建立一套新颖、多功能并且自由的作业系统。 Fedora是商业化的Red Hat Enterprise Linux发行版的上游原始码。 2、Fedora软件安装 64 位 .deb&a…

Python pandas数据分析

Python pandas数据分析&#xff1a; 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其…

Linux shell编程学习笔记29:shell自带的 脚本调试 选项

Linux shell脚本的调试方法比较多&#xff0c;上次我们探讨和测试了shell内建命令set所提供的一些调试选项&#xff0c;其实 shell 本身也提供了一些调试选项。我们以bash为例来看看。 1 bash 的命令行帮助信息&#xff08;bash --help&#xff09; purleEndurer csdn ~ $ ba…

带你用uniapp从零开发一个仿小米商场_3.animate.css动画库在uniapp中的使用

这篇文章仅做简单介绍animate.css动画库在uniapp中如何使用 animate.css动画库引入 怕有人没看专栏前面的文章,所以这里重新介绍一边animate.css动画库的引入,知道的可以跳过 可以在这里下载 animate.css动画库官网http://www.animate.net.cn/ 下载好animate.css后在项目根…

5.2每日一题(无穷级数敛散性:绝对收敛、比较法/比较法的极限形式、p级数)

一般看到绝对收敛和条件收敛——先看级数绝对值的敛散性

ubuntu修改系统语言

修改ubuntu系统语言 操作指令修改系统设置总结 操作 ubuntu系统自带的英文环境&#xff0c;个人觉得用起来不方便。改掉吧。换成中文 指令修改 参考了一些博客的解决方式 ctrlartT 打开终端。 sudo apt-get install language-pack-zh-hans 输入下载汉化包的指令。 但是&…

SpringMVC系列-7 @CrossOrigin注解与跨域问题

背景 前段时间帮同事分析了一个跨域问题&#xff0c;正好系统分析和整理一下。 1.跨域 理解同源策略是理解跨域的前提。同源策略定义如下&#xff1a; 在同一来源的页面和脚本之间进行数据交互时&#xff0c;浏览器会默认允许操作&#xff0c;而不会造成跨站脚本攻击&#x…

ImportError: No module named python_util.util

ImportError: No module named python_util.util 从师兄那里拷贝了一个python文件&#xff0c;运行报错ImportError: No module named python_util.util&#xff0c;python小白上网搜&#xff0c;并没有搜到有价值的答案。。。经过摸索&#xff0c;最后解决。 其实这就是缺少…

vue3+elementPlus之侧边菜单栏功能

选择默认的颜色&#xff0c;将代码拷贝至<el-aside>模块中 稍微把不需要的修改一下。 <template><div class"common-layout"><el-container><el-header class"homeHeader"><div class"headerTitle">Devops…

抖音权重查询源码H5源码

源码下载&#xff1a;123网盘

Linux的基本指令 ( 一 )

目录 前言 Linux基本指令 快速认识五个指令 ls指令 补充内容 pwd指令 补充内容 cd指令 补充内容 重新认识指令 指令的本质 which指令 alias指令 最后 一个文件的三种时间 tree指令及安装 tree指令 前言 关于Linux操作系统的桌面&#xff0c;在学校教学中我们…

共享模型之内存

JMM JMM&#xff1a;Java内存模型。定义了主存&#xff08;所有线程共享的数据&#xff09;、工作内存&#xff08;每个线程对应的私有数据&#xff09;的抽象概念。 JMM存在以下几个特征 原子性&#xff1a;保证指令不会受到线程上下文切换所影响。可见性&#xff1a;保证指…

C语言—什么是数组名

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int arr[]{1,2,3,4};printf("%p\n",arr);printf("%p\n",&arr);printf("%p\n",*arr);return 0; } 结论&#xff1a;数组名是数组首元素地址&#xff08;下标为0的元素…

PC端从零搭建微信自动回复机器人(一)基础框架搭建及源码

由于工作需要&#xff0c;最近一年一直在研究和使用C#&#xff0c;加上最近工作上有做微信机器人的需要&#xff0c;在已经对接、调试稳定之后&#xff0c;将项目的源码分享给大家&#xff0c;传递开源精神。 一、环境依赖 1、开发工具&#xff1a;Vistual Studio 2022 2、Ne…

【FFmpeg视频解码】解码数据结构及函数总结

转载自原文地址&#xff1a;https://www.cnblogs.com/wangguchangqing/p/5744941.html 本文的总结分为以下两个部分&#xff1a; 数据读取&#xff0c;主要关注在解码过程中所用到的FFmpeg中的结构体。解码过程中所调用的函数 在学习的过程主要参考的是dranger tutorial&…

从零开始搭建博客网站-----源代码试部署

拿到了该项目的源码&#xff0c;先尝试是否可以成功部署&#xff0c;详细的部署视频地址 后端项目部署 先把maven配置好&#xff0c;都改成自己下载的maven地址 文件编码改成utf-8&#xff0c;防止配置文件乱码 如果maven是刚下的&#xff0c;要改一下下载包的地址&#xff0…

使用信息面板沟通研发工作

凌鲨里面的内容面板里面有专门针对研发团队的白板功能&#xff0c;它可以把文档&#xff0c;图片&#xff0c;软件设计&#xff0c;需求&#xff0c;任务/缺陷等相关研发要素串接起来。 使用 你还可以调整背景颜色。 引用项目内数据 点击面板中的连接会在右侧打开对应内容

电子学会C/C++编程等级考试2021年12月(二级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:统计指定范围里的数 给定一个数的序列S,以及一个区间[L, R], 求序列中介于该区间的数的个数,即序列中大于等于L且小于等于R的数的个数。 时间限制:1000 内存限制:65536输入 第一行1个整数n、,分别表示序列的长。(0 < n…