栈与队列的相互实现

news2024/11/15 14:05:14

文章目录

  • 前言
  • 一、用队列实现栈
  • 二、用栈实现队列
  • 总结


梦想不是别人给你的目标,靠自己的意志去实现的才是梦想… ——《食梦者》

前言

嗨喽喽!大家好哇。欢迎小伙伴们来到我的博客!!
在这里插入图片描述
在前面已经分享了栈与队列两种数据结构的特点与其相关的代码实现。那么今天将来分享一下使用其中一种数据结构来实现另外一种数据结构解决方法吧(虽然在实际场景中并没有什么卵用,但主要是存在一定的教学意义)。即使用队列实现栈使用栈实现队列。那么就赶快开始吧!!

一、用队列实现栈

首先呈上在刷题网站力扣上的相关算法题的链接:【用队列实现栈】。
在这里插入图片描述
由题意可知,我们要使用队列这一数据结构来实现栈即其相关的基本操作的方法。那么此时我们就可以使用CV大法,将我们先前手搓的队列直接COPY到题目当中(当然,没有实现过栈的小伙伴最好先手撕一遍)!!

先给出队列的相关代码:

Queue:

typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* _pNext;//指向队列的下一个
	QDataType _data;
}QNode;

typedef struct Queue
{
	QNode* _front;//指向队头
	QNode* _rear;//指向队尾
	int _size;
}Queue;

void QueueInit(Queue* q)
{
	assert(q);
	q->_front = q->_rear = NULL;
	q->_size = 0;
}

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* del;
	while (q->_front != q->_rear)
	{
		del = q->_front;
		q->_front = q->_front->_pNext;
		free(del);
		del = NULL;
	}
	free(q->_rear);
	q->_front = q->_rear = NULL;
}

void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* node = (QNode*)malloc(sizeof(QNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	node->_data = x;
	node->_pNext = NULL;
	if (q->_rear)//队列不为空
	{
		q->_rear->_pNext = node;
	}
	else//队列为空
	{
		q->_front = node;
	}
	q->_rear = node;
	q->_size++;
}

void QueuePop(Queue* q)
{
	assert(q && q->_size);
	if (q->_front->_pNext)//队列不仅一个元素
	{
		QNode* next = q->_front->_pNext;
		free(q->_front);
		q->_front = NULL;
		q->_front = next;
	}
	else//队列仅一个元素
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
	}
	q->_size--;
}

QDataType QueueFront(Queue* q)
{
	assert(q && q->_front);
	return q->_front->_data;
}

QDataType QueueBack(Queue* q)
{
	assert(q && q->_rear);
	return q->_rear->_data;
}

bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->_size == 0;
}

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

我们知道队列的特性是先进先出,而栈的特性则是后进先出。为了再现栈后进先出的特性,由于队列无论是出队列,还是入队列,其先后顺序是一致的。因此我们可以创建两个队列,始终将数据入非空的队列,模仿入栈。再通过将非空队列中除队尾的元素全部入到空队列(一边入队,一边让原队列的数据出队),然后让仅剩下一个元素的队列出队,模仿出栈,至于访问栈顶元素,就可以直接访问非空的队列的尾元素即可。

知道了问题的解决方法,那么接下来就可以手撕代码:
首先使用两个队列来声明模拟的栈:

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

然后是模拟的栈的初始化:

MyStack* myStackCreate() {
    MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&mystack->q1);
    QueueInit(&mystack->q2);
    return mystack;
}

入栈,通过对非空的队列进行入队操作来模仿:

void myStackPush(MyStack* obj, int x) {
    if (!QueueEmpty(&obj->q1))//q1不为空
    {
        QueuePush(&obj->q1, x);
    }
    else                     //q2不为空
    {
        QueuePush(&obj->q2, x);
    }
}

出栈,通过假设法来找到空与非空队列,然后将非空队列中除队尾的元素都入到空队列中,模拟找到栈顶元素,保存下来,再让其出队,始终让至少一个队列为空,最后返回保存的元素:

int myStackPop(MyStack* obj) {
    //假设法
    Queue* empty = &obj->q1;
    Queue* nonempty = &obj->q2;
    if (!QueueEmpty(empty))//q1不为空
    {
        empty = &obj->q2;
        nonempty = &obj->q1;
    }
    //不为空的前size-1导走,剩下的为栈顶
    while (QueueSize(nonempty) > 1)//当非空队列仅剩下一个元素时退出
    {
        QueuePush(empty, QueueFront(nonempty));//将非空队列的头元素放到空队列中
        QueuePop(nonempty);
    }
    int ret = QueueFront(nonempty);
    QueuePop(nonempty);
    return ret;
}

获取栈顶元素,直接返回非空队列的队尾元素即可:

int myStackTop(MyStack* obj) {
    if (!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

栈的判空,看两队列是否都为空即可:

bool myStackEmpty(MyStack* obj) {
    if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2))
    {
        return true;
    }
    else
    {
        return false;
    }
}

最后是模拟栈的销毁:

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

之后提交即可,过了!!
在这里插入图片描述

二、用栈实现队列

首先呈上在刷题网站力扣上的相关算法题的链接:【用栈实现队列】
在这里插入图片描述
既然成功使用队列实现了栈,那么接下来就反过来,使用栈实现队列。
同样的,先使用CV大法将先前实现过的栈拷贝到题中。

typedef int STDataType;

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


void STInit(ST* pst)
{
	assert(pst);
	
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

void STDestroy(ST* pst)
{
	assert(pst);
	
    if (pst->capacity == 0)
        return;
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

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++;
}

void STPop(ST* pst)
{
	assert(pst && pst->top);

	pst->top--;
}

bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

STDataType STTop(ST* pst)
{
	assert(pst && pst->top);

	return pst->a[pst->top - 1];
}

int STSize(ST* pst)
{
	assert(pst);
	
	return pst->top;
}

与上一题类似的是,这里我们也需要使用到两个栈来进行模拟。

但不同的是,这里需要让其中一个栈始终模拟入队列,让另一个栈始终模拟出队列,从而实现队列的基本操作。即通过始终让数据入到模拟入队列的栈中,来模仿入队操作。出队列时,将模拟入队的栈中的数据,全部入到模拟出队列的栈中,再对模拟出队列的栈进行出栈操作。获取队头元素,模拟出队列的栈的栈顶元素即为队头,当然若模拟出队列的栈为空,则需将模拟出队列的栈中的元素入到模拟入队列的栈中再进行操作。

了解了问题的解决方法,接下来就是紧张刺激的手撕代码环节:
首先,通过定义两个栈(模拟入队的栈模拟出队的栈,以下分别用pushst和popst表示)来声明模拟的队列:

typedef struct {
    ST pushst;//模拟入队的栈
    ST popst;//模拟出队的栈
} MyQueue;

然后是模拟队列的初始化:

MyQueue* myQueueCreate() {
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&obj->pushst);
    STInit(&obj->popst);

    return obj;
}

入队列,直接嵌套一个入栈的方法(对pushst入栈),一行代码解决:

void myQueuePush(MyQueue* obj, int x) {//入队
    STPush(&obj->pushst, x);
}

出队列,通过将pushst中的元素全部入到popst,直到pushst为空,保存popst的栈顶元素,并删除栈顶元素,最后返回该栈顶元素即可:

int myQueuePop(MyQueue* obj) {//出队
    if (STEmpty(&obj->popst))//若popst为空,将pushst压入栈popst中
    {
        while (!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst, STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }
    int front = STTop(&obj->popst);
    STPop(&obj->popst);
    return front;
}

获取队头元素,先判断popst是否为空,为空就先将pushst中的元素全部入到popst中,最后返回popst的栈顶元素即可:

int myQueuePeek(MyQueue* obj) {
    if (STEmpty(&obj->popst))//若popst为空,将pushst压入栈popst中
    {
        while (!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst, STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }
    return STTop(&obj->popst);
}

模拟队列的判空操作,判断两个栈是否都为空即可:

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}

最后是模拟队列的销毁操作:

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);
    free(obj);
}

之后提交,过了!!
在这里插入图片描述

总结

若本篇博客对小伙伴有帮助的话,希望能点个赞!
完!!!ヾ( ̄▽ ̄)ByeBye

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

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

相关文章

进程和计划任务以及步骤

进程 进程和程序有关,把该文件放到内存里,进程是动态的,不同时刻的状态不一样 内存:放置正在运行的程序和所需数据的位置 程序启动 ——》将相关文件和数据放到内存里 ——》进程(processes) 进程相关命令 …

easyexcel动态表头导出

动态表头导出excel 红框固定&#xff0c;绿框动态 引入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency>工具类 import com.alibaba.excel.util…

0.2 模拟电视简介

0.2 模拟电视简介 模拟电视指从图像信号的产生、传输、处理到接收机的复原&#xff0c;整个过程几乎都是在模拟体制下完成的电视系统或电视设备。模拟电视使用模拟信号传输图像和音频&#xff0c;模拟信号的幅度、相位和频率分别表示图像的亮度、颜色和声音。模拟电视的优点&a…

jmeter -n -t 使用非GUI模式运行脚本说明

命令模式下执行jmx文件 jmeter -n -t fatie.jmx -l results\t4.jtl -e -o results\h1 表示以命令行模式运行当前目录下的脚本fatie.jmx,将结果存入当前目录下的results\t1.jtl,并且生成html格式的报告&#xff0c;写入文件夹results\h1。 说明&#xff1a;生成结果的文件夹r…

在离线单机或内网环境中快速安装Visual Studio 2022并还原用户设定

20240606 By wdhuag 目录 前言 参考&#xff1a; 在外网环境下载离线安装包 1、在已安装好VS的电脑上用Visual Studio Installer导出配置.vsconfig 2、下载在线安装包VisualStudioSetup_Enterprise_2022.exe到D:\VisualStudio\ 3、使用cmd定位到VisualStudioSetup_Enter…

MySQL深分页,limit 100000,10 优化

文章目录 一、limit深分页为什么会变慢二、优化方案2.1 通过子查询优化&#xff08;覆盖索引&#xff09;回顾B树结构覆盖索引把条件转移到主键索引树 2.2 INNER JOIN 延迟关联2.3 标签记录法&#xff08;要求id是有序的&#xff09;2.4 使用between...and... 我们日常做分页需…

docker 命令 ps,inspect,top,logs详解

docker常用命令教程-4 docker ps docker ps 命令用于列出当前正在运行的容器。默认情况下&#xff0c;它只显示正在运行的容器&#xff0c;但你可以使用 -a 或 --all 选项来显示所有容器&#xff08;包括已停止的容器&#xff09;。 常用的选项和示例&#xff1a; -a 或 --…

手机自动化测试:4.通过appium inspector 获取相关app的信息,以某团为例,点击,搜索,获取数据等。

0.使用inspector时&#xff0c;一定要把不相关的如weditor啥的退出去&#xff0c;否则&#xff0c;净是事。 1.从0开始的数据获取 第一个位置&#xff0c;有时0.0.0.0&#xff0c;不可以的话&#xff0c;你就用这个。 第二个位置&#xff0c;抄上。 直接点击第三个启动。不要…

手持终端RFID电子标签读写器超高频手持机

RFID手持机具备RFID读写功能&#xff0c;可以对RFID标签进行识读&#xff0c;是有特定功能的PDA(便携式移动终端)。 作为现代化信息管理工具的重要组成部分&#xff0c;其强大的功能和便捷的操作性正在越来越多的领域得到应用。从物流仓储到零售管理&#xff0c;从生产制造到医…

浅谈申请小程序地理位置权限的正确打开方式

小程序地理位置接口有什么功能&#xff1f; 这篇内容会教大家如何快速申请“获取当前的地理位置&#xff08;onLocationChange&#xff09;”接口&#xff0c;以便帮助大家顺利开通接口。以下内容是本人经历了多次的申请经历得出来的经验&#xff0c;来之不易&#xff0c;望大家…

Cy5.5-甘氨鹅去氧胆酸荧光染料标记物:一种生物成像工具

在现代生物研究和医学诊断领域&#xff0c;荧光染料标记物扮演着举足轻重的角色。它们能够实现对生物体内特定分子或细胞的非侵入性、实时追踪&#xff0c;从而为我们揭示生命过程的奥秘提供工具。 Cy5.5-甘氨鹅去氧胆酸荧光染料标记物的结构与特性 Cy5.5-甘氨鹅去氧胆酸荧光染…

Google Inventor一款适合儿童的趣味APP编程开发平台

Google Inventor 其实是指 App Inventor&#xff0c;这是由 Google 开发并由麻省理工学院&#xff08;MIT&#xff09;继续维护和发展的一个应用程序开发平台。 App Inventor 的主要特点和用途如下&#xff1a; 什么是 App Inventor&#xff1f; App Inventor 是一个基于 W…

Python文本处理利器:jieba库全解析

文章目录 Python文本处理利器&#xff1a;jieba库全解析第一部分&#xff1a;背景和功能介绍第二部分&#xff1a;库的概述第三部分&#xff1a;安装方法第四部分&#xff1a;常用库函数介绍1. 精确模式分词2. 全模式分词3. 搜索引擎模式分词4. 添加自定义词典5. 关键词提取 第…

渗透测试报告生成工具

目录 1.前言 1.1 渗透测试报告是什么? 1.2 渗透测试报告的编写需要考虑以下几点&#xff1a; 1.3 一份优秀的渗透测试报告应该具备以下特点&#xff1a; 1.4 在编写渗透测试报告之前&#xff0c;需要进行一些准备工作&#xff1a; 1.5 渗透测试报告一般包括以下部分&…

【成品设计】基于STM32和LoRa的大棚监测控制系统

《基于STM32和LoRa的大棚监测控制系统》 整体功能&#xff1a; LoRa智能远程控制系统是基于STM32F103C8T6最小系统板如图1的基础上使用STM32CubeMX工具搭建工程&#xff0c;基于HAL库进行编程设计&#xff0c;采用主 - 从模式来进行设计模拟与实现&#xff0c;主机、从机上都…

各平台对象存储

一、阿里云对象存储 官方文档&#xff1a;https://help.aliyun.com/zh/oss/getting-started/getting-started-with-oss?spma2c4g.11186623.0.0.299a646c6nWWcW 1.引入maven 官网&#xff1a;https://help.aliyun.com/zh/oss/developer-reference/java-installation?spma2c…

跨越文化鸿沟:海外短剧推广平台多语言支持技术的深度探讨

在海外短剧推广的过程中&#xff0c;多语言支持技术不仅是跨越语言障碍的桥梁&#xff0c;更是连接不同文化背景的纽带。本文将深入探讨多语言支持技术的重要性&#xff0c;以及其在实现过程中的技术细节和策略。 一、多语言支持技术的文化价值 多语言支持技术不仅能让观众跨…

材料科学领域科技查新点提炼方法!---附案例

材料科学是研究材料的组织结构、性质、生产流程、使用效能及它们之间的相互关系的科学&#xff0c;集物理学、化学、冶金学等于一体。随着科技的发展&#xff0c;纳米技术和生物技术也广泛应用到该领域中。从材质上可以分为金属材料、无机非金属材料、有机高分子材料和复合材料…

review of c++

友元关系是单向的。 指针