【数据结构】有关栈和队列相互转换问题

news2024/11/28 18:43:51

文章目录

  • 用队列实现栈
    • 思路
    • 实现
  • 用栈实现队列
    • 思路
    • 实现


用队列实现栈

Leetcode-225 用队列实现栈
在这里插入图片描述

思路

建立队列的基本结构并实现队列的基本操作

这部分这里就不多说了,需要的可以看笔者的另一篇博客
【数据结构】队列详解(Queue)
就简单带过一下需要实现的功能

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

// 队尾插入
void QueuePush(Queue* pq, QDataType x);
// 队头删除
void QueuePop(Queue* pq);

// 取队头和队尾的数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);

//获取队列长度
int QueueSize(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);


使用两个队列实现
一个空的来实现操作,一个装数据来存储

我们以几个简单操作来理解一下

  • 栈的以队列实现的基本结构及初始化

因为队列的结构及功能是初始化的条件,在上一步中我们已经实现完成了
所以关于栈的结构只需要一个能够存储两个队列的结构体就行了

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

对于栈的初始化,因为我们要实现离开初始化操作后,两个队列和栈都需要存在,所以我们需要使用malloc为其创建内存

而两栈也是需要额外开辟空间的,这同样是在第一步中完成,这里笔者就不多说了,同样也是由malloc来创建内存的

MyStack* myStackCreate() {
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}
  • 尾插

因为要进行一系列操作,所以我们必须确保其中的一个队列为空。在这个基础上,我们可以对已有数据的队列进行操作,去实现功能。

所以我们就只需要判断两个队列中哪个为空,往另一个不为空的队列里加数据就行了

void myStackPush(MyStack* obj, int x) {
    if (!QueueEmpty(&obj->q1)) {
        QueuePush(&obj->q1, x);
    } else
        QueuePush(&obj->q2, x);
}
  • 尾删

作为一个栈,我们需要实现的是先进后出,所以这里是尾删,关于尾删,我们的想法应该是利用队列中带有的尾插操作,将有数据的队列的数据一个个传到空队列中,最后一个传出的数据就是我们要删的数

我们可以理解下图为尾插过5个数的队列
在这里插入图片描述
利用队列的尾插及头删操作,每将队列1的队头尾插给队列2后,头删队列1,知道队列1仅剩一个数据,两个队列的状态就会如下
在这里插入图片描述
此时我们需要做的是利用队列中自带的取队头数据操作,就可以获得栈所需的栈顶元素,

栈顶对应队尾

然后就是需要理解的重点,我们可以发现如果把上图队列1的队头删除后,队列2就是进行尾删的队列1,这样就实现了栈顶删除的操作

如此一来,也呼应了刚刚我们尾插中,找不为空的队列进行尾插的操作

实现

整个队列实现栈的操作中最难理解的就是尾插和尾删的联系与衔接,这两者的关系一旦理清,别的就是信手拈来,这里笔者就不赘述了,跟栈的原始操作大同小异,代码就放在下面了

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
typedef int QDataType;
typedef struct QueueNode {
    struct QueueNode* next;
    QDataType val;
} QNode;

typedef struct Queue {
    QNode* phead;
    QNode* ptail;
    int size;
} Queue;
// 初始化
void QueueInit(Queue* pq) {
    assert(pq);
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}
// 销毁
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;
}

// 队尾插入
void QueuePush(Queue* pq, QDataType x) {
    assert(pq);

    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    if (newnode == NULL) {
        perror("malloc newnode fail");
    }
    newnode->val = x;
    newnode->next = NULL;

    if (pq->ptail == NULL) {
        pq->phead = newnode;
        pq->ptail = newnode;
    } else {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
    pq->size++;
}
// 队头删除
void QueuePop(Queue* pq) {
    assert(pq);
    assert(pq->size != 0);

    if (pq->size == 1) {
        free(pq->phead);
        pq->phead = pq->ptail = NULL;
    } else {
        QNode* next = pq->phead->next;
        free(pq->phead);
        pq->phead = next;
    }
    pq->size--;
}

// 取队头和队尾的数据
QDataType QueueFront(Queue* pq) {
    assert(pq);
    assert(pq->phead);
    return pq->phead->val;
}
QDataType QueueBack(Queue* pq) {
    assert(pq);
    assert(pq->ptail);
    return pq->ptail->val;
}

int QueueSize(Queue* pq) {
    assert(pq);
    return pq->size;
}
bool QueueEmpty(Queue* pq) {
    assert(pq);
    return pq->size == 0;
}



//从这里开始用队列实现栈
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

//初始化
MyStack* myStackCreate() {
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}

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

//尾删
int myStackPop(MyStack* obj) {
    Queue* empty = &obj->q1;
    Queue* notempty = &obj->q2;
    if (!QueueEmpty(&obj->q1)) {
        empty = &obj->q2;
        notempty = &obj->q1;
    }

    while (QueueSize(notempty) > 1) {
        QueuePush(empty, QueueFront(notempty));
        QueuePop(notempty);
    }
    int top = QueueFront(notempty);
    QueuePop(notempty);

    return top;
}

//获取栈顶,即队尾元素
int myStackTop(MyStack* obj) {
    if (!QueueEmpty(&obj->q1)) {
        return QueueBack(&obj->q1);
    } else {
        return QueueBack(&obj->q2);
    }
}

//判空
bool myStackEmpty(MyStack* obj) {
    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);
*/

用栈实现队列

Leetcode-232 用栈实现队列

在这里插入图片描述

思路

同理,先实现栈的结构设计及相关操作

关于用栈实现队列,我们要做的不同方向是,利用两个栈实现,这两个栈不想上面一样,通过判空来使用,而是直接进行分区,一个用来输入,一个用来输出

栈的相关实现,看笔者的另一篇博客也一样,这里也不多说了

【数据结构】栈详解

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;

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

// 初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

// 入栈  出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);

// 取栈顶数据
STDataType STTop(ST* pst);

// 判空
bool STEmpty(ST* pst);
// 获取数据个数
int STSize(ST* pst);

  • 初始化

同样,我们需要做的也是利用内存分配的方法来实现队列和两个栈的基本结构

初始化两个栈ininin和outoutout,出队直接就往ininin压栈,出队先检查outoutout里面有没有元素,有的话outoutout的栈顶就是队首,否则就先把ininin里面的元素弹出压入到outoutout中。这样就能保证outoutout的栈顶始终都是队首,ininin的栈顶始终都是队尾。


typedef struct {
    ST instack;
    ST outstack;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&pq->instack);
    STInit(&pq->outstack);
    return pq;
}
  • 尾插及头删

在上面我们提到过了,我们需要建立一个输入栈和一个输出栈,所以关于尾插,没什么好多讲的,就往输入队列里一直加新元素就行了

至于头删这就很关键了,我们用图来理解,假设尾插几个数据后队列如下
在这里插入图片描述

因为头删我们需要拿到的数据是A,所以我们利用栈的尾插将每个数据插到输出栈中,即
在这里插入图片描述

此时A在栈顶我们可以提取出来或者删除

如果只是一般逻辑的话,我们大可将这个输出栈的数据倒回输入栈,然后重复原来操作,这其实是可以的,但是这会大大提高原来的时间复杂度,从而降低效能

那么怎么改呢

假设我们不倒回,而是直接往输入栈中加入数据,其实可以发现,输出栈的栈底其实连着的输入栈的栈底,即

在这里插入图片描述
因此,我们不用再倒回输出栈,只需要在需要利用栈顶元素时判断输出栈是否为空,如果为空就将输入栈的数据补充给输出栈就行了

实现

typedef int STDataType;

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

// 初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

// 入栈  出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);

// 取栈顶数据
STDataType STTop(ST* pst);

// 判空
bool STEmpty(ST* pst);
// 获取数据个数
int STSize(ST* pst);

// 初始化和销毁
void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	// top指向栈顶数据的下一个位置
	pst->top = 0;

	// top指向栈顶数据
	//pst->top = -1;

	pst->capacity = 0;
}

void STDestroy(ST* pst)
{
	assert(pst);

	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");
			return;
		}

		pst->a = tmp;
		pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}

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

	pst->top--;
}


// 取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

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

// 判空
bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

// 获取数据个数
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

//初始化两个栈ininin和outoutout,出队直接就往ininin压栈,出队先检查outoutout里面有没有元素,有的话outoutout的栈顶就是队首,否则就先把ininin里面的元素弹出压入到outoutout中。这样就能保证outoutout的栈顶始终都是队首,ininin的栈顶始终都是队尾。
typedef struct {
    ST instack;
    ST outstack;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&pq->instack);
    STInit(&pq->outstack);
    return pq;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->instack, x);
}

void inTOout(MyQueue* obj)
{
    while(!STEmpty(&obj->instack))
    {
        STPush(&obj->outstack, STTop(&obj->instack));
        STPop(&obj->instack);
    }
}
int myQueuePop(MyQueue* obj) {
    if(STEmpty(&obj->outstack))
    {
        inTOout(obj);
    }
    int ret = STTop(&obj->outstack);
    STPop(&obj->outstack);
    return ret;
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->outstack))
    {
        inTOout(obj);
    }
    int ret = STTop(&obj->outstack);
    return ret;
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->instack) && STEmpty(&obj->outstack);
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->instack);
    STDestroy(&obj->outstack);
    free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

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

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

相关文章

金融业开源软件应用 评估规范

金融业开源软件应用 评估规范 1 范围 本文件规定了金融机构在应用开源软件时的评估要求&#xff0c;对开源软件的引入、维护和退出提出了实现 要求、评估方法和判定准则。 本文件适用于金融机构对应用的开源软件进行评估。 2 规范性引用文件 下列文件中的内容通过文中的规范…

数据科学:使用Optuna进行特征选择

大家好&#xff0c;特征选择是机器学习流程中的关键步骤&#xff0c;在实践中通常有大量的变量可用作模型的预测变量&#xff0c;但其中只有少数与目标相关。特征选择包括找到这些特征的子集&#xff0c;主要用于改善泛化能力、助力推断预测、提高训练效率。有许多技术可用于执…

【kettle012】kettle访问FTP服务器文件并处理数据至PostgreSQL(已更新)

1.一直以来想写下基于kettle的系列文章,作为较火的数据ETL工具,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下FTP服务器相关知识体系 3.欢迎批评指正,跪谢一键三连! kettle访问FTP服务器文件并处理数据至PostgreS…

【Unity UI系统介绍】

Unity UI系统介绍 想了解更多游戏开发知识,可以扫描下方二维码,免费领取游戏开发4天训练营课程 Unity UI 是 Unity 引擎中的一套用户界面&#xff08;UI&#xff09;系统&#xff0c;允许开发者创建和管理游戏的用户界面。 Canvas&#xff1a;Canvas 是 Unity UI 的核心组件…

如何3分钟快速训练一个属于自己的(暴躁老哥、猫娘)AI大模型?(弱智吧经典问题为例)

首先我们进入谷歌Gemini&#xff1a;Gemini - Google DeepMind 然后我们进入官网看见左边的几个选项 选择Create new prompt创建新的提示 选择结构化提示 点击action可以增加列&#xff0c;也就是设置更多回答或者选项 那么那个温度&#xff08;Temperature&#xff09;是什么…

二、安装、使用Grafana

目录 一、安装Grafana 二、使用grafana 一、安装Grafana 官网&#xff1a;https://grafana.com/ 账号&#xff1a;admin 密码&#xff1a;xxxxxx [rootrabbitmq_2 prometheus]# [rootrabbitmq_2 prometheus]# wget https://dl.grafana.com/enterprise/release/grafana-enter…

redis安装与群集

项目需求&#xff1a; 1.安装redis 2.测试redis性能&#xff0c;100个并发连接&#xff0c;100000个请求测试 3.在当前数据库下创建键值对 a11,a22,a33,a44&#xff0c;a55 4.查看键值对 5.将a1改名为a11,将a2删除 5.将a3移动到1号数据库 6搭建redis集群(可选) 测试环境…

第六十节 Java设计模式 - 过滤器/标准模式

Java设计模式 - 过滤器/标准模式 过滤器模式使用不同的条件过滤对象。 这些标准可以通过逻辑操作链接在一起。 过滤器模式是一种结构型模式。 例子 import java.util.List; import java.util.ArrayList;class Employee {private String name;private String gender;private…

【Spring】Springmvc学习Ⅲ

# Spring&#xff4d;vc学习Ⅲ 文章目录 一、图书管理系统1. 功能1.1 登录前端接口前端代码后端接口后端代码 1.2 图书列表展示步骤:图书类代码mock数据代码控制层调用代码服务层代码&#xff08;存储除数据库中需要存储的数据&#xff09; 2. 分层控制2.1 三层架构2.2 代码重…

C语言实现简单的日历功能

开篇 本篇文章的题目来源于《编程珠玑》第三章课后习题的第四个问题&#xff0c;也是我会手动实现的本章的最后一个功能。 问题概要 给定月和年&#xff0c;使用字符数组生成该月的日历&#xff08;含有周几的日历&#xff09;。 思路分析 为了生成给定年份中某个月的日历&…

【光线重塑技术】小姐姐,美得不可方物——lllyasviel/ic-light

在英伟达自18年宣布光追技术之后&#xff0c;RTX显卡也成了目前Steam游戏的常客。就连 AMD、Intel 和 Apple Silicon 都宣布要在GPU上支持光追算法。这次我要介绍的是huggingface上比较火的relight技术—— ic-light 介绍 IC-Light 是一个操纵图像照明的项目。 IC-Light &qu…

商务分析方法与工具(十):Python的趣味快捷-公司财务数据最炫酷可视化

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

【FreeRTOS 快速入门】-- 1、STM32工程移植FreeRTOS

目录 一、新建STM32工程 为了示范完整的移植过程&#xff0c;我们从0开始&#xff0c;新建一个标准的STM32点灯工程。 &#xff08;本篇以CubeMX作示范&#xff0c;CubeIDE操作近同&#xff0c;可作对比参考&#xff09; 1、新建工程 选择 芯片型号 新建工程 2、搜索芯片型号…

Pytest自动化测试框架完美结合Allure

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 简介 Allure Framework是一种灵活的、轻量级、多语言测试报告工…

weblogic [WeakPassword]

一、漏洞描述 开放了wblogic端口&#xff0c;进去发现有任意读取文件漏洞&#xff0c;配合解密工具读出密码&#xff0c;登录后台传入webshell 二、影响版本 当前版本 三、影响组件 weblogic 四、漏洞判断 hello/file.jsp?path/etc/passwd 发现有任意文件下载 五、漏洞…

C++牛客周赛题目分享(2)小红叕战小紫,小红的数组移动,小红的素数合并,小红的子序列求和

目录 ​编辑 1.前言 2.四道题目 1.小红叕战小紫 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思路 2.小红的数组移动 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思路 3.小红的素数合并 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思…

2024年淘宝天猫618超级红包领取口令活动时间是从什么时候开始到几月几号结束?

2024年淘宝天猫618活动&#xff0c;将于2024年5月19日开始&#xff0c;今年618淘宝天猫取消了预售环节。同时&#xff0c;618淘宝天猫也提供了多项优惠活动&#xff1a;超级红包、跨店满减、官方立减、全程价保及草柴APP领优惠券拿购物返利等多重优惠活动。 2024年淘宝天猫618…

【C语言】指针(一)

目录 一、内存 1.1 ❥ 理解内存和地址的关系 1.2 ❥ 编址 二、指针变量 2.1 ❥ 取地址操作符&#xff08;&&#xff09; 2.2 ❥ 指针变量和解引用操作符&#xff08;*&#xff09; 2.3 ❥ 指针变量的大小 三、指针类型的意义 3.1 ❥ 指针的解引用 3.2 ❥ 指针-整数 3…

智能制造装备业项目数字化管理之项目模板管理

智能制造装备&#xff0c;作为工业4.0的核心组成部分&#xff0c;正日益受到全球制造业的关注。这类装备融合了信息技术和制造技术&#xff0c;旨在提高生产效率、降低成本并增强产品的个性化。然而&#xff0c;随着智能制造装备行业的飞速发展&#xff0c;项目管理复杂性也在不…

AI图像生成-原理

一、图像生成流程总结 【AI绘画】深入理解Stable Diffusion&#xff01;站内首个深入教程&#xff0c;30分钟从原理到模型训练 买不到的课程_哔哩哔哩_bilibili 二、如果只是用comfy UI生成图片 1、找到下面几个文件&#xff0c;把对应模型移动到对应文件夹即可使用 2、选择对…