用队列实现栈和用栈实现队列

目录

  • 用队列实现栈
    • 创建栈
    • 实现入栈
    • 实现出栈
    • 判空
    • 取栈顶元素
    • 释放
  • 用栈实现队列
    • 创建队列
    • 入队
    • 出队
    • 返回队列开头的元素
    • 判空
    • 释放

前面我们实现了栈和队列,其实栈和队列之间是可以相互实现的

下面我们来看一下 用 队列实现栈用栈实现队列

用队列实现栈

使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ,否则返回 false

如图是2个队列和1个栈
在这里插入图片描述
此时我们已经向栈中插入了5个元素,因为用队列去模拟,所以元素存放在栈中

因为用2个队列实现栈,所以我们创建一个MyStack的结构体,里面的元素是2个队列

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

创建栈

因为函数会返回创建栈的地址,如果定义的这个栈为局部遍历,则出函数后这个值就会被销毁,所以我们需要将这个栈开辟到堆区

然后也调用队列中的初始化函数,队栈结构体中2个队列进行开辟和初始化

MyStack* myStackCreate() {
    MyStack* new = (MyStack*)malloc(sizeof(MyStack));
    if(new==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    QueueInit(&new->q1);
    QueueInit(&new->q2);
    return new;
}

实现入栈

在非空的队列中插入元素,就相当于入栈了

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

实现出栈

假设我们现在要出栈,按照栈的特点,先出的应该是5,但是由于队列的特点,只能先进先出,所以我们可以把最后一个元素之前的所有元素都出队并放到为空的那个队列中,再将最后一个元素出队,就做到的出栈的操作
在这里插入图片描述

所以这里需要找到空的那个队列,将有元素的队列中前面元素都放到空队列中后,再将最后一个元素出队,这样还是一个空队列一个有元素队列
所以一直会有一个空队列和一个非空队列
在这里插入图片描述

根据这个思路我们实现一下代码

首先要判断出空队列和非空队列,如果直接使用if else语句就会是语句冗余

所以我们可以定义emptyQnonemptyQ指针表示空队列和非空队列,然后判断出q1和q2哪个才是空队列和非空队列,emptyQ指向空队列,nonemptyQ指向非空队列

	Queue* emptyQ = &obj->q1;
    Queue* nonemptyQ = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        nonemptyQ = &obj->q1;
        emptyQ = &obj->q2;
    }

然后将非空队列中最后一个元素前的所有元素放入空数组中

	while(QueueSize(nonemptyQ)>1)
    {
        QueuePush(emptyQ,QueueFront(nonemptyQ));
        QueuePop(nonemptyQ);
    }

最后返回原非空队列中唯一的那个值,然后将它出队列

完整代码:

int myStackPop(MyStack* obj) {
    Queue* emptyQ = &obj->q1;
    Queue* nonemptyQ = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        nonemptyQ = &obj->q1;
        emptyQ = &obj->q2;
    }

    while(QueueSize(nonemptyQ)>1)
    {
        QueuePush(emptyQ,QueueFront(nonemptyQ));
        QueuePop(nonemptyQ);
    }
    int front = QueueFront(nonemptyQ);
    QueuePop(nonemptyQ);
    return front;
}

判空

当两个队列都为空,那么模拟的栈也为空

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

取栈顶元素

元素都存在非空队列中,先找到非空队列,然后调用队列的QueueBack取出队列中最后一个值

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

其实这里可以体现出在实现队列时,设计QueueBack函数的原因


释放

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

用栈实现队列

使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)
实现 MyQueue类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false

我们先将一些数据入到一个栈中
在这里插入图片描述
如果我们想实现出队,首先出队的数据应该是1,但是根据栈的特点,1是最后一个出

所以需要将栈中非栈底元素全都入栈到另一个栈中,根据栈的特点,这些元素到另一个栈中后,元素的顺序对于队列来说就是“正的了”

在这里插入图片描述
所以对于这个栈,它的出栈顺序和要模拟的队列出队顺序是一样的

如果想入栈,如果入到之前“顺序正常”的栈中,则会改变最后出队的顺序

所以从上面的分析可以看出,2个栈可以单独负责一项任务,一个栈负责入队,一个负责出队

在定义MyQueue时,直接定义一个叫PushStack和一个叫PopStack的栈

typedef struct {
    ST PushStack;
    ST PopStack;
} MyQueue;

在这里插入图片描述

只要是入队,就往PushStack中入

当出队时,如果PopStack为空,就需要把PushStack中的数据先出栈,在入栈到PopStack中,然后出PopStack的栈顶元素
在这里插入图片描述
如果PopStack不为空,出队其实就是直接从PopStack中出栈,出PopStack的栈顶元素

所以可以看出,最终都是出PopStack的栈顶元素,只是如果PopStack为空,就需要将元素放到PopStack中,在实现时判断PopStack是否为空

所以下面我们来实现一下:


创建队列

MyQueue* myQueueCreate() {
    MyQueue*new = (MyQueue*)malloc(sizeof(MyQueue));
    if(new ==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    STInit(&new->PushStack);
    STInit(&new->PopStack);

    return new;

}

入队

根据前面的分析,入队就是将元素入栈到PushStack

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

出队

如果PopStack为空,就需要把PushStack中的元素都放到PopStack

if(STEmpy(&obj->PopStack))
    {
        while(obj->PushStack.top>0)
        {
            STPush(&obj->PopStack,STTop(&obj->PushStack));
            STPop(&obj->PushStack);
        }
    }

如果PopStack为不为空,就直接出栈顶元素

 int top = STTop(&obj->PopStack);
    STPop(&obj->PopStack);
    return top;

完整代码:

int myQueuePop(MyQueue* obj) {
    if(STEmpy(&obj->PopStack))
    {
        while(obj->PushStack.top>0)
        {
            STPush(&obj->PopStack,STTop(&obj->PushStack));
            STPop(&obj->PushStack);
        }
    }

    int top = STTop(&obj->PopStack);
    STPop(&obj->PopStack);
    return top;
}

返回队列开头的元素

其实返回队列开头的元素的操作与上面出队列操作类似

PopStack为空时,就将PushStack中的元素出栈,再依次入栈到PopStack
PopStack不为空时,就直接返回栈顶元素

可以看出,这个操作与出队列的操作唯一有差距的一点就是,不用pop掉PopStack中的栈顶元素

int myQueuePeek(MyQueue* obj) {
    if(STEmpy(&obj->PopStack))
    {
        while(obj->PushStack.top>0)
        {
            STPush(&obj->PopStack,STTop(&obj->PushStack));
            STPop(&obj->PushStack);
        }
    }

    int top = STTop(&obj->PopStack);
   
    return top;
}  

判空

如果当PushStackPopStack都为空的话,模拟出的队列才为空

bool myQueueEmpty(MyQueue* obj) {
    return STEmpy(&obj->PushStack)&&STEmpy(&obj->PopStack);
}

释放

调用STDestroy函数,销毁模拟队列的2个栈,然后再free掉队列

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->PushStack);
    STDestroy(&obj->PopStack);
    free(obj);
    obj = NULL;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows创建用户,添加到管理员组,添加到远程桌面组、RDP

原因&目的 在获得反弹shell后无法得到明文密码,无法远程桌面登录 在目标机器创建新的账号,且为管理员账号,可以远程桌面登录 cmd /c net user gesila 123 /add cmd /c net localgroup Administrators gesila /add cmd /c net localgro…

优思学院 | 质量工程师的职责有哪些?

质量工程师,是一位肩负着质量管理、质量控制和质量改进使命的职业人员。他们身负使命,不断探究、发现、改进,为企业打造出更加卓越、可靠的产品和服务。 在大多数企业中,质量工程师是一个非常重要的职位,他们的职责在…

智能立体车库plc以太网无线应用

一、项目背景 此项目为平面移动类智能停车库,是以传感器网络为支撑的物联网智能停车管理系统。比较于传统的停车场模式,智能立体车库不仅占地少,空间利用率高,智能化程度高,采用集约化系统化的车位管理、收费管理&…

Vue3 集成Element3、Vue-router、Vuex实战

第一步:使用Vite快速搭建Vue 3项目 基础操作总结: npm init vite-app 项目名称cd 项目名称npm installnpm run dev 温馨提示:如果还是Vue 2 环境请参考:Vue 2 升级Vue3 ,并且使用vsCode 搭建Vue3 开发环境 Vue 3 …

ARM uboot 启动 Linux 内核

一、编译厂商提供的 uboot 此处,我使用的是九鼎提供的 uboot : 二、烧录 uboot 到 SD 卡 进入 uboot 的 sd_fusing 目录,执行命令烧写 uboot : ./sd_fusing.sh /dev/sdb。 三、将 SD 卡插入开发板,进入 uboot 按任意…

操作系统-内存管理

一、总论 1.1 硬件术语 ​ 为了不让读者懵逼(主要是我自己也懵逼),所以特地整理一下在后面会用到术语。 ​ 我们电脑上有个东西叫做内存,他的大小比较小,像我的电脑就是 16 GB 的。它是由 ROM 和 RAM 组成的&#x…

RK3588平台开发系列讲解(同步与互斥篇)信号量介绍

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、信号量介绍二、信号量API1、结构体2、API三、函数调用流程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢上一章我们看了自旋锁的原理,本章我们一起学习下信号量的用法。 一、信号量介绍 和自旋锁一样,…

渗透测试综合实验(迂回渗透,入侵企业内网并将其控制为僵尸网络)

第1节 实验概述 1.1 实验背景概述 本实验为模拟真实企业环境搭建的漏洞靶场,通过网络入侵Web服务器,拿到控制权限后发现有内网网段,建立隧道做内网穿透,接着进一步扫描内网主机,并进行漏洞利用,最终通过域…

java登录页面验证码的生成以及展示

1、代码示例 后端返回前端一个字节数组。 2、gif图面展示 网页中一张图片可以这样显示&#xff1a; <img src“http://www.jwzzsw.com/images/log.gif”/>也可以这样显示&#xff1a; <img src“data:image/gif;base64,R0lGODlhAgACAIAAAP///wAAACwAAAAAAgACAAACAo…

聚会Party

前言 加油 原文 聚会常用会话 ❶ He spun his partner quickly. 他令他的舞伴快速旋转起来。 ❷ She danced without music. 她跳了没有伴乐的舞蹈。 ❸ The attendants of the ball are very polite. 舞会的服务员非常有礼貌。 ❶ Happy birthday to you! 祝你生日快乐!…

Linux--高级IO--poll--0326

1. poll #include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); poll只负责等。 参数介绍 fds 是一个结构体类型的地址&#xff0c;相比于select中的fd_set类型,pollfd结构体可以内部封装一些遍历&#xff0c;解决需要关系那些文件描述符&#…

ES6新特性保姆级别教程【建议收藏】

文章目录1、ECMAScript 6 简介1.1、ECMAScript 和 JavaScript 的关系1.2、ES6 与 ECMAScript 2015 的关系1.3、ECMAScript 的历史2、let 和 const 命令2.1、let 命令2.1.1、基本用法2.1.2、不存在变量提升2.1.3、不允许重复声明2.1.4、暂时性死区2.2、const 命令2.2.1、基本用法…

cuda学习4-6

4. Hardware Implementation NVIDIA GPU架构是围绕一系列可扩展的多线程流式多处理器&#xff08;SM&#xff09;构建的。当主机CPU上的CUDA程序调用内核网格时&#xff0c;网格的块将被枚举并分配给具有可用执行能力的多处理器。线程块的线程在一个多处理器上并发执行&#x…

C++内存管理详解

大家好&#xff0c;这里是bang_bang&#xff0c;今天来分享下内存管理的知识。 目录 1.C/C内存分布 2.C内存管理方式 2.1new/delete操作内置类型 2.2new/delete操作自定义类型 3.operator new与operator delete函数 3.1operator new 3.2operator delete 4.new和delete的实现…

367. 有效的完全平方数 ——【Leetcode每日一题】

367. 有效的完全平方数 给你一个正整数 num 。如果 num 是一个完全平方数&#xff0c;则返回 tru &#xff0c;否则返回 false 。 完全平方数 是一个可以写成某个整数的平方的整数。换句话说&#xff0c;它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数&#xf…

Mybatis框架源码笔记(九)之反射工具类解析

1 反射工具类 Java中的反射功能虽然强大&#xff0c;但是代码编写起来比较复杂且容易出错。Mybatis框架提供了专门的反射包&#xff0c;对常用的反射操作进行了简化封装&#xff0c;提供了更简单方便的API给调用者进行使用&#xff0c;主要的反射包代码结果如下&#xff1a; …

React 组件的 children 数据使用

children 属性表示该组件的子节点&#xff0c;只要组件内部有子节点&#xff0c;props 就有该属性&#xff0c;是自动带上的&#xff0c;不需要开发者添加。 children 可以是 普通文本、普通标签元素、函数、JSX … 案例一&#xff1a;普通文本 import React from "rea…

奇异值分解(SVD)和图像压缩

在本文中&#xff0c;我将尝试解释 SVD 背后的数学及其几何意义&#xff0c;还有它在数据科学中的最常见的用法&#xff0c;图像压缩。 奇异值分解是一种常见的线性代数技术&#xff0c;可以将任意形状的矩阵分解成三个部分的乘积&#xff1a;U、S、V。原矩阵A可以表示为&#…

数据结构与算法基础(王卓)(21):哈夫曼编码(1):过程

逻辑雏形 根据老师讲解的思路&#xff0c;梳理出程序运行的逻辑雏形如下&#xff1a; 搞一个多维数组HC&#xff0c;用来存储我们这里 n(每) 个节点的哈夫曼编码搞一个数组cd&#xff0c;用来存储我们这里每个节点是前面一位的左子树&#xff08;0&#xff09;还是右子树&…

Spark 基本知识介绍

文章目录1. Spark是什么2. Spark与Hadoop区别3. Spark四大特点3.1 速度快3.2 易于使用3.3 通用性强3.4 运行方式4. Spark整体框架5. Spark运行模式6. Spark架构角色6.1 YARN角色6.2 Spark 角色1. Spark是什么 Spark是用于大规模数据处理的统一分析引擎。 Spark 最早源于一篇论…
最新文章