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

文章目录

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


用队列实现栈

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);} elseQueuePush(&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);} elseQueuePush(&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.mzph.cn/diannao/11414.shtml

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

相关文章

信息量、熵、KL散度、交叉熵概念理解

信息量、熵、KL散度、交叉熵概念理解 (1) 信息量 信息量是对事件的不确定性的度量。 假设我们听到了两件事&#xff0c;分别如下&#xff1a;事件A&#xff1a;巴西队进入了世界杯决赛圈。 事件B&#xff1a;中国队进入了世界杯决赛圈。仅凭直觉来说&#xff0c;显而易见事件…

Python从0到POC编写-魔法方法

name __name__ 是系统定义的内部函数&#xff0c; 它的作用是识别模块。 通常我们看到这样一句话&#xff1a; if __name__ __main____name__ 的值有两种情况&#xff0c;那么挨个来说下。 如果模块是被直接执行的 &#xff0c;那么 __name__ 的值 为 __main__ 例如&…

html中用frameset对窗口进行划分

html中&#xff0c;一般有<head><body>等部分&#xff0c;在用<frameset>对窗口进行区域划分时&#xff0c;<body>标签对就不再需要了。直接删除就可以了。 请看下面的示例&#xff1a; 由于使用frameset进行窗口划分时&#xff0c;对于电脑屏幕的划分…

排序-快速排序(Quick Sort)

快排的简介 快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;采用分治法的策略&#xff0c;其基本思想是选择一个基准元素&#xff0c;通过一趟排序将待排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据要…

Android 老年模式功能 放大字体

1 配置属性 <attr name"text_size_16" format"dimension"/><attr name"text_size_18" format"dimension"/><attr name"text_size_14" format"dimension"/><attr name"text_size_12&quo…

容器组件:Column ,Row(HarmonyOS学习第四课【4.1】)

容器组件-Column Column 容器组件是沿垂直方向布局的容器。该组件从APIVersion7开始支持从API version 9开始&#xff0c;该接口支持在ArkTs,卡片中使用。其可以包含子组件 Column(value?: {space?: string | number}) 参数 space 参数类型string | number 是否必填&am…

第11节 多文件函数生成位置规律

我把多年的shellcode开发经验浓缩在了这个专题课&#x1f469;&#x1f3fb;‍&#x1f4bb;里&#xff0c;主要面向对网络安全技术感兴趣的小伙伴。这是视频版内容对应的文字版材料&#xff0c;内容里面的每一个环境我都亲自测试实操过的记录&#xff0c;有需要的小伙伴可以参…

进程创建-fork

demo1代码状态变迁 demo2代码输出到stdout使用管道 demo1 代码 #include <pthread.h> #include <iostream> #include <unistd.h> int main(int argc, char const *argv[]) {// 1.pid_t x fork();// 2.pid_t y fork();// 3.printf("%d %d\n", x…

【C++】STL-list的使用

目录 1、list的使用 1.1 list的构造 1.2 list的遍历 1.3 list capacity 1.4 list element access 1.5 容量相关 list是一个带头双向循环链表 1、list的使用 1.1 list的构造 1.2 list的遍历 list只有两种遍历方式&#xff0c;因为没有operator[] 因为list的双向链表&am…

《建筑抗震设计规程》DB62/T 3055-2020下载

本规程适用于抗震设防分类为乙类、丙类&#xff0c;抗震设防烈度为6度(0.05g)、7度(0.10g、0.15g)、8度(020g、030g)、9度(0.40g)的多高层钢筋混凝土结构及钢-混凝土混合结构、中等跨度钢屋盖结构、门式刚架钢结构、多低层砖砌体结构房屋建筑的抗震设计。 关于甘肃省地方标准《…

01 | 为什么需要消息队列?

哪些问题适合使用消息队列来解决&#xff1f; 1. 异步处理 2. 流量控制 使用消息队列隔离网关和后端服务&#xff0c;以达到流量控制和保护后端服务的目的。 3. 服务解耦 无论增加、减少下游系统或是下游系统需求如何变化&#xff0c;订单服务都无需做任何更改&#xff0c…

【主题广泛|稳定检索】2024年社会科学、公共服务与人文艺术国际会议(SPSHA 2024)

2024年社会科学、公共服务与人文艺术国际会议&#xff08;SPSHA 2024&#xff09; 2024 International Conference on Social Sciences, Public Services, and Humanities and Arts 【会议简介】 本次会议定于2024年在中国的繁华都市——广州召开&#xff0c;汇聚了全球在该领…

如何挑选护眼灯?分享最好的台灯品牌排行榜

作为家长&#xff0c;孩子的健康无疑是我们最为牵挂的事项。然而&#xff0c;通过研究数据显示&#xff0c;我国青少年儿童的近视率高达52.7%&#xff0c;这意味着在每十名儿童中&#xff0c;就有超过半数的孩子可能面临视力问题。这一数据无疑令人警觉。当我们看到孩子们在写作…

Web3加密空投入门:空投类型有哪些?如何避免限制?

今天分享空投如何避免限制以提高效率&#xff0c;增加成功几率&#xff0c;首先我们来了解什么是空投加密&#xff0c;有哪些空投类型。 一、什么是空投加密&#xff1f; 加密货币空投是一种营销策略&#xff0c;包括向用户的钱包地址发送免费的硬币或代币。 加密货币项目使用…

中医揿针的注意事项

点击文末领取揿针的视频教程跟直播讲解 关于揿针的注意事项&#xff0c;我们可以从以下几个方面进行探讨&#xff1a; 01操作前准备 1. 确保针具的清洁和无菌状态&#xff0c;以避免感染。 2. 了解患者的身体状况&#xff0c;如是否有特殊疾病或过敏史&#xff0c;以便选择…

解码管理新趋势:咨询公司如何助力企业破局升级?

随着数字化、智能化技术的不断发展&#xff0c;企业管理的边界正在被重新定义。传统的层级管理正在向扁平化、网络化转变&#xff0c;员工的参与度和自主性得到了前所未有的提升。同时&#xff0c;企业也更加注重数据驱动决策&#xff0c;通过大数据分析来洞察市场变化&#xf…

STM32 PWM 计数器模式和对齐

STM32 PWM 计数器模式和对齐 1. TIM高级定时器简介2. TIM计数模式2.1 向上计数2.2 向下计数2.3 中心对齐模式&#xff08;向上/向下计数&#xff09;2.4 重复计数 3. PWM输出模式3.1 举例看下PWM中心对齐模式&#xff0c;设置参数如下&#xff1a; 4. FOC中PWM相关设置说明4.1 …

SpringBoot对接微信公众平台(1)--- 配置微信公众平台测试号URL并校检

SpringBoot对接微信公众平台&#xff08;1&#xff09;--- 配置微信公众平台测试号URL并校检 说明微信公众号接口测试号申请后端代码实现内网穿透接口测试 说明 这里记录下自己学习SpringBoot对接微信公众平台的成长过程&#xff0c;以防止后面继续踩坑且方便以后直接使用。这…

房价暴跌到头?中国楼市回调信号愈发强烈!

一、房地产市场的繁荣与萧条 在过去的几十年里&#xff0c;中国的房地产市场经历了多次繁荣与萧条的周期。自2015年第二季度起&#xff0c;全国房价开始逐渐攀升&#xff0c;这标志着新一轮房地产市场的繁荣期的开始。以深圳为例&#xff0c;新政出台后&#xff0c;这座城市的…

CSS 实现文本的渐变色

定义一个类 .text-color{/* 创建一个水平方向的颜色渐变 */background: linear-gradient(120deg, #bd34fe 30%,#5c34fe, #41d1ff);/* 将文本透明度设置为0&#xff0c;以便背景渐变可见 */color: transparent;/* 使用背景渐变来填充文本背景 */-webkit-background-clip: text;…