具备教学意义的实操(用队列实现栈)

news2024/11/18 8:40:49

225. 用队列实现栈 - 力扣(LeetCode)https://leetcode.cn/problems/implement-stack-using-queues/description/

实现逻辑

一个是先进先出(队列),一个是后进先出(栈)

这里用两个队列导入·一下数据

出来数据之后入数据

代码的实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{
	QNode* _phead;//头节点
	QNode* _ptail;//尾结点
	int size;
}Queue;
// 这里采取一级指针进行实现代码逻辑,如果不创建队列的结构,我们就需要采取二级指针
// 初始化队列 
void QueueInit(Queue* ps);
// 销毁队列 
void QueueDestroy(Queue* ps);
// 队尾入队列 
void QueuePush(Queue* ps, QDataType data);
// 队头出队列 
void QueuePop(Queue* ps);
// 获取队列头部元素 
QDataType QueueFront(Queue* ps);
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps);
// 获取队列中有效元素个数 
int QueueSize(Queue* ps);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps);

// 初始化队列 
void QueueInit(Queue* ps)
{
	ps->_phead = NULL;
	ps->_ptail = NULL;
	ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{
	assert(ps);
	QNode* cur = ps->_phead;
	while (cur)
	{
		//存下下一个节点的地址,不会出现找空的情况
		QNode* next = cur->_next;
		free(cur);
		cur =next;
	}
}
// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush:newnode:error:");
		exit(1);
	}
	//把需要插入的数值插入到节点里面
	newnode->_next = NULL;
	newnode->_data = x;
	
	// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。
	// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail 
	// 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL 
	// 来检查队列中是否只有一个元素。

	//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较
	//ps->_phead == NULL
	if (ps->size == 0)
	{
		ps->_phead = ps->_ptail = newnode;
	}
	else
	{
		//尾插节点
		ps->_ptail->_next = newnode;
		ps->_ptail= newnode;
	}
	ps->size++;
}
// 队头出队列 
void QueuePop(Queue* ps)
{
	assert(ps && ps->size);
	// 改变头结点
	ps->_phead = ps->_phead->_next;
	ps->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_phead->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_ptail->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{
	return ps->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->size == 0;
}

//匿名结构体(我们需要两个队列,所以采取结构体嵌套的形式解决问题)
typedef struct 
{
    Queue Q1;
    Queue Q2;
} MyStack;

//初始化(关键在于,初始化的数值出去之后就销毁,所以我们需要创建节点进行初始化)
MyStack* myStackCreate() 
{
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    if(obj == NULL)
    {
        perror("obj:error:");
        exit(1);
    }
    QueueInit(&(obj->Q1));
    QueueInit(&(obj->Q2));
    return obj;
}
//入栈 
void myStackPush(MyStack* obj, int x) 
{
    if(QueueEmpty(&(obj->Q1)))//q1为空
    {
        QueuePush(&(obj->Q2), x);
    }
    else
    {
        QueuePush(&(obj->Q1), x);
    }
}

//删除栈顶元素(核心逻辑)
int myStackPop(MyStack* obj) 
{
    //判空(假设法假设q1为空)
    MyStack* Empty = &(obj->Q1);
    MyStack* noEmpty = &(obj->Q2);
    if(QueueEmpty(&(obj->Q2)))//如果q1不为空,q2为空,此时颠倒一下
    {
        Empty = &(obj->Q2);
        noEmpty = &(obj->Q1);
    }
    //循环导入
    while(QueueSize(noEmpty) > 1)//这里的关键点是你不知道谁是空,不是空
    {
        QueuePush(Empty, QueueFront(noEmpty));//这里 是把不空的数值头部元素,循环导入到空的数值里面去
        QueuePop(noEmpty);//并且进行出栈操作
    }
    //结束之后留下了一个,我们保存下来返回这个节点,并且最后进行删除
    int ret = QueueFront(noEmpty);
    QueuePop(noEmpty);
    return ret;
}

//取出栈顶元素
int myStackTop(MyStack* obj) 
{
    if(QueueEmpty(&(obj->Q1)))//q1为空
    {
        return QueueBack(&(obj->Q2));
    }
    else
    {
        return QueueBack(&(obj->Q1));
    }
}
//判空
bool myStackEmpty(MyStack* obj) 
{
    //q1 q2都为空的时候才为空
    return (QueueEmpty(&(obj->Q1)) && QueueEmpty(&(obj->Q2)));
}
//释放空间
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&(obj->Q1));
    QueueDestroy(&(obj->Q2));
    free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

这段代码使用两个队列 Q1Q2 来实现一个栈 MyStack。下面是对每个部分的详细解释:

  1. 数据结构定义

    • QDataType:定义了队列中存储的数据类型,这里使用 int
    • QNode:定义了队列中的节点,包含数据 _data 和指向下一个节点的指针 _next
    • Queue:定义了队列结构,包含指向队列头部和尾部的指针 _phead 和 _ptail,以及记录队列大小的 size
  2. 队列操作函数

    • QueueInit:初始化队列,将头尾指针设置为 NULL,大小设置为0。
    • QueueDestroy:释放队列中所有节点的内存。
    • QueuePush:在队列尾部添加一个新节点。
    • QueuePop:移除队列头部的节点,并减少队列大小。
    • QueueFront:返回队列头部的元素。
    • QueueBack:返回队列尾部的元素。
    • QueueSize:返回队列中元素的数量。
    • QueueEmpty:检查队列是否为空。
  3. 栈操作函数

    • myStackCreate:创建一个新的 MyStack 实例,初始化两个队列 Q1 和 Q2
    • myStackPush:如果 Q1 为空,则将新元素推入 Q2;否则推入 Q1
    • myStackPop:如果 Q2 不为空且 Q1 为空,则交换两个队列的角色。然后,将 Q2 中的元素逐个移动到 Q1,除了最后一个元素。最后,返回并移除 Q2 中的最后一个元素。
    • myStackTop:返回栈顶元素。如果 Q1 不为空,返回 Q1 的尾部元素;否则返回 Q2 的尾部元素。
    • myStackEmpty:如果两个队列都为空,则返回 true,表示栈为空。
    • myStackFree:销毁 MyStack 实例,释放所有相关联的内存。
  4. 使用队列实现栈的逻辑

    • 通过两个队列 Q1 和 Q2 的交替使用,实现了栈的后进先出(LIFO)特性。当一个队列为空时,可以将另一个队列中的元素移动过来,最后一个元素就是栈顶元素。
    • myStackPush 函数根据 Q1 是否为空来决定将新元素推入哪个队列,这样做的目的是保持两个队列中的元素数量大致相等,从而在 myStackPop 操作中能够高效地将元素从一个队列移动到另一个队列。
  5. 注意事项

    • 在 myStackPop 和 myStackTop 函数中,需要检查两个队列的状态,以确定当前栈顶元素所在的队列。
    • myStackDestroy 函数中,确保先销毁队列,再释放 MyStack 实例的内存。

这种使用两个队列实现栈的方法利用了队列的先进先出特性来模拟栈的后进先出特性。这种方法的好处是可以在 O(1) 时间内完成栈的基本操作,同时避免了使用数组实现栈时可能需要的数组扩展和收缩操作。

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

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

相关文章

论文研读 An Image Is Worth 16x16 Words: Transformers For Image Recognition At Scale

完整翻译 《An Image is Worth 16x16 Words》完整版翻译_an image is worth 16*16words-CSDN博客 大神讲解 Vision Transformer详解-CSDN博客 视频讲解 11.1 Vision Transformer(vit)网络详解_哔哩哔哩_bilibili 学习整理 简要概述&#xff1a;Vision Transformer&#xff…

Flutter3.x get-cli中运行get init初始化项目报错如何处理

Flutter get-cli中运行get init初始化项目会提示如下错误&#xff1a; get init s E:\flutter\flutter study\tempstudy\misapp01> get init 1)Getx Pattern (by Kau) 2)CLEAN (by Arktekko) which architecture do you want to use? [1] unhandled exception: Synchromu…

谁使用DITA?

▲ 搜索“大龙谈智能内容”关注公众号▲ Keith根据LinkedIn上的数据进行的统计&#xff0c;主要反应的西方世界使用DITA的公司。因为LinkedIn在国内不能访问&#xff0c;笔者认为针对中国的数据并不准确。 作者 | John Walker - NXP销售和市场营销业务分析师 2013年4月18日 …

Java --- 集合(2)--- 这篇文章让你学会如何使用List集合

本期文章来自黑马程序员以及Java入门到精通&#xff0c;希望各位大佬发现文章的瑕疵及时表出&#xff0c;另外也感谢您的收看。话不多说&#xff0c;直接进入正题...... 目录 一.List集合的使用&#xff1a; 二.三种遍历List方式&#xff1a; 首先还是给大家呈现这幅图&#x…

AI宝库:全球精选AI工具网站一网打尽,创新智慧触手可及

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 精选专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 探秘AI之巅&#xff0c;一键解锁未来工具&#xff01;立即点击&#xff0c;开启智能新纪元&#…

即插即用篇 | YOLOv8引入PSAModule | 高效金字塔压缩注意力模块

本改进已集成到 YOLOv8-Magic 框架。 最近研究表明,通过在深度卷积神经网络中嵌入注意力模块可以有效地提高网络性能。在这项工作中,提出了一种新的轻量级且有效的注意力方法,名为金字塔挤压注意力(PSA)模块。通过在ResNet的瓶颈块中用PSA模块替换3x3卷积,得到了一种新的…

系统图表:洞察数据的价值与魅力

在数字化、信息化迅猛发展的今天&#xff0c;数据已经成为企业决策、科学研究、社会管理等领域的核心资源。而如何高效、准确地理解和利用这些数据&#xff0c;成为摆在我们面前的重要课题。系统图表作为数据可视化的重要呈现工具&#xff0c;不仅能帮助我们洞察数据的内在规律…

Raspberry Pi Connect 官方树莓派远程连接新方式

系列文章目录 前言 2024 年 5 月 7 日 戈登-霍林沃思 今天&#xff0c;我们很高兴地宣布 Raspberry Pi Connect 测试版发布&#xff1a;这是一种安全、易用的方法&#xff0c;只需使用网络浏览器&#xff0c;就能从地球上的任何地方远程访问您的 Raspberry Pi。 远程访问 Rasp…

基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (二)

基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; #Options local_llm llama3 llm ChatOllama(modellocal_llm, format"json", temperature0) #embeddings #embeddings OllamaEmbeddings(model"nomic-embed-text") embed…

[算法][差分][延迟相差][leetcode]2960. 统计已测试设备

题目地址&#xff1a; https://leetcode.cn/problems/count-tested-devices-after-test-operations/description/ 解法一&#xff1a;暴力解法 class Solution {public int countTestedDevices(int[] batteryPercentages) {//特殊条件判断if(null batteryPercentages || ba…

PCIE协议-2-事务层规范-Message Request Rules

2.2.8 消息请求规则 本文档定义了以下几组消息&#xff1a; INTx 中断信号电源管理错误信号锁定事务支持插槽电源限制支持厂商定义消息延迟容忍度报告&#xff08;LTR&#xff09;消息优化缓冲区冲洗/填充&#xff08;OBFF&#xff09;消息设备就绪状态&#xff08;DRS&#…

单细胞分析:多模态 reference mapping (2)

引言 本文[1]介绍了如何在Seurat软件中将查询数据集与经过注释的参考数据集进行匹配。我们展示了如何将来自不同个体的人类骨髓细胞&#xff08;Human BMNC&#xff09;的人类细胞图谱&#xff08;Human Cell Atlas&#xff09;数据集&#xff0c;有序地映射到一个统一的参考框…

IP定位技术在打击网络犯罪中的作用

随着互联网的普及和信息技术的发展&#xff0c;网络犯罪日益猖獗&#xff0c;给社会治安和个人财产安全带来了严重威胁。而IP定位技术的应用为打击网络犯罪提供了一种有效手段。IP数据云将探讨IP定位技术在打击网络犯罪中的作用及其意义。 1. IP定位技术的原理 IP&#xff08…

【小白可懂】SpringBootWeb入门

web开发需要的技术栈&#xff1a; 前端web开发&#xff1a; html css javascript Vue Element Nginx 后端web开发&#xff1a; Maven SpringBoot Web 基础篇 MySOL SpringBoot Mybatis SpringBoot Web开发篇 SpringBoot web进阶篇 什么是spring&#xff1f; 官网&a…

静态分析-RIPS-源码解析记录-03

既然有源码可以debug&#xff0c;那么直接跑测试用例&#xff0c;来跟踪处理逻辑感觉比直接看代码理逻辑更快一些&#xff0c;尤其是涉及到了扫描阶段&#xff0c;不然不容易弄清某刻某个变量的取值。 对于所有漏洞而言&#xff0c;都是由sink点到source点检测是否有过滤函数&…

创作纪念日(一周年)

机缘 我进入CSDN成为创作者是去年2023年的五月份&#xff0c;当时是在学校报名了蓝桥杯单片机组的比赛&#xff0c;觉得单片机方面有许多精妙之处&#xff0c;并且自学初学单片机实在有许多奇巧的设计点&#xff0c;有许多编程与硬件实际运行需要磨合的地方&#xff0c;这些惊…

idea中使用git拉取代码详细操作

注意&#xff1a;解决 Git拉取代码和本地代码丢失问题请点这里查看 以textGit文件为例&#xff1a; 下图&#xff1a;本地刚拉取远程的代码 git上的代码 1、在本地对代码进行修改 2、在git上对代码进行修改&#xff0c;模拟其他人对此文件的提交修改 3、拉取远程代码 4、合并自…

栈实现队列

一、分析 栈的特点是先出再入&#xff0c;而队列的特点为先入先出&#xff0c;所以我们创造两个栈&#xff0c;一个用来存放数据&#xff0c;一个用来实现其它功能此时栈顶为队尾&#xff1b;当要找队头数据时将前n-1个数据移入到另一个栈中&#xff0c;此时剩余那个数据为队头…

多模态CLIP和BLIP

一、CLIP 全称为Contrastive Language-Image Pre-Training用于做图-文匹配&#xff0c;部署在预训练阶段&#xff0c;最终理解为图像分类器。 1.背景 以前进行分类模型时&#xff0c;存在类别固定和训练时要进行标注。因此面对这两个问题提出CLIP&#xff0c;通过这个预训练…

制造业如何选择合适的项目管理软件?(内含软件推荐)

近期&#xff0c;收到很多小伙伴的提问&#xff1a;“想了解制造行业如何选择到合适的项目管理软件&#xff1f;”在竞争激烈的市场环境中&#xff0c;有效的项目管理对于制造业的发展至关重要&#xff0c;而项目管理软件则是重要支撑&#xff0c;能帮助企业更好地规划和跟踪项…