【栈与队列面试题】用队列实现栈(动图演示)

news2024/12/23 7:11:35

两个队列实现一个栈

前言:

💥🎈个人主页:​​​​​​Dream_Chaser~ 🎈💥

✨✨刷题专栏:http://t.csdn.cn/UlvTc

⛳⛳本篇内容:力扣上栈与队列的面试OJ题目

目录

两个队列实现一个栈

队列的实现(前提准备)

一.题目描述

1.自定义栈的结构

2.自定义栈的初始化

3.将元素压入栈顶

4.移除并返回栈顶元素

5.返回栈顶元素

6.判断栈是否为空栈

7.栈的销毁

自定义栈的代码实现 


队列的实现(前提准备)

#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
typedef int QDataType;
typedef struct QueueNode
{	
	QDataType data;
	struct QueueNode* next;
}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);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0



void QueueInit(Queue* pq)
{
	assert(pq);// 检查指针是否为空

	pq->phead=NULL; // 将队列的头指针置为空
	pq->ptail = NULL;// 将队列的头指针置为空
	pq->size = 0;// 将队列的头指针置为空
}
 
// void QPrint(Queue* pq)
// {
// 	assert(pq);

// 	QNode* cur = pq->phead;
// 	QNode* next = cur;
// 	while (cur != NULL)
// 	{
// 		printf("%d ", cur->data);
// 		cur = cur->next;
// 	}
// }
 
void QueueDestroy(Queue* pq)
{
	assert(pq);// 检查指针是否为空
	
    QNode* cur = pq->phead;// 创建一个指针 cur,指向队列的头指针
	while (cur)
	{
		QNode* next = cur->next;// 创建一个指针 cur,指向队列的头指针
		free(cur);// 释放当前节点的内存
		cur = next;// 将指针 cur 移动到下一个节点
	}
	
    pq->phead = pq->ptail = NULL;// 将队列的头指针和尾指针置为空
	pq->size = 0;// 将队列的大小置为0
}
 
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	
    QNode* newnode = (QNode*)malloc(sizeof(QNode));// 创建一个新的节点
 
	if (newnode == NULL)
	{
		perror("malloc fail\n");// 检查内存分配是否成功
		return;
	}
	
    newnode->data = x;// 设置新节点的数据为传入的元素值
	newnode->next = NULL;// 将新节点的指针域置空
 
	//一个节点
	//多个节点
	if (pq->ptail == NULL)// 判断队列是否为空
	{
		//assert(pq->phead == NULL);// 如果队列为空,头指针也应为空
		
        pq->phead = pq->ptail = newnode;// 将新节点同时设置为队列的头节点和尾节点
	}
	
    else
	{
		pq->ptail->next = newnode;// 将新节点同时设置为队列的头节点和尾节点
		pq->ptail = newnode;// 更新队列的尾指针为新节点
	}
	pq->size++;// 增加队列的大小计数
}
 
void QueuePop(Queue* pq)
{
	assert(pq);// 检查指针是否为空
	assert(!QueueEmpty(pq));// 检查队列是否非空
	//assert(pq->phead);// 检查队列的头指针是否存在
	//1.一个节点
	if (pq->phead->next == NULL) // 队列只有一个节点的情况
	{
		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(!QueueEmpty(pq));// 检查队列是否非空
	//assert(pq->phead);// 检查队列的头指针是否存在
 
	return pq->phead->data;// 返回队列头节点的数据
}
  
QDataType QueueBack(Queue* pq)
{
	assert(pq);// 检查队列是否非空
	assert(!QueueEmpty(pq));// 检查队列是否非空
	//assert(pq->phead);// 检查队列的头指针是否存在
	
	return pq->ptail->data;//返回队列尾节点的数据
}


 
int QueueSize(Queue* pq)
{
	assert(pq);//检查指针是否为空
 
	return pq->size;//返回队列的大小(元素个数)
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	
	//方法一,将队列的头指针以及尾指针置空
	//return pq->phead = NULL && pq->ptail==NULL;
	
	//方法二,将队列的有效元素置空
	return pq->size == 0;
}

一.题目描述

来源:225. 用队列实现栈 - 力扣(LeetCode)

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

需要了解的知识:

栈(Stack):

  1. 后进先出(LIFO)顺序:最后压入栈的元素,第一个弹出栈。它像一堆盘子,最后放上的盘子,先拿走。

  2. 在栈顶添加,在栈顶删除:所有新元素都放在栈顶,操作栈时只有栈顶元素可以被访问。

  3. 压栈和出栈操作不可逆:当一个元素被压入栈后,要等到它到栈顶位置才能将其弹出。栈不允许非栈顶元素先出栈。

  4. 有大小限制:每个栈都有最大容量限制,栈满时不能继续压入新元素。

  5. 主要操作:栈主要支持两种操作 - 入栈(push)和出栈(pop),所有元素都从栈顶压入/弹出

  6. 适合处理后进先出的任务:栈这种后进先出的特性,适合模拟那些需要后操作的先完成的场景,如函数调用堆栈。

总结:是一种后进先出的线性表,只允许在表的一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的元素最后才能取出,具有先进后出的逻辑顺序。

队列(Queue):

  1. 先进先出(FIFO)顺序:先进入队列的元素会先出队列,后进入的元素后出队列。

  2. 在队尾添加,在队头删除:所有新元素都添加在队尾,删除元素时都从队头删除元素。

  3. 出队操作不可取消:已进入队列的元素不能中途删除,只能等待它到队头后再删除它

  4. 有大小限制:队列通常有最大容量限制,队列满时不能再加入新元素,必须等待队头元素出队后才能加入。

  5. 主要操作:队列主要支持两种操作 - 入队(enqueue)和出队(dequeue),入队是将元素加入队尾,出队是从队头删除元素。

  6. 适合处理先进先出的任务:队列适合去处理需要先进先出顺序的任务,比如打印任务队列、排队等。

总结:是一个先进先出、有顺序的线性表,它只允许在一端进行插入,在另一端进行删除,遵循先进先出的原则。这使其非常适合模拟现实生活中的排队等情形。

1.自定义栈的结构

   MyStack 这个结构体模拟了一个栈的数据结构,它使用两个队列 q1 和 q2 来实现栈的功能。

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

2.自定义栈的初始化

        使用 malloc 函数为 MyStack 结构体对象分配内存空间。sizeof(MyStack) 表示要分配的内存大小等于 MyStack 结构体的大小。返回的指针类型是 void* 。

        因此需要进行类型转换为 MyStack* 类型并将其赋值给 obj 变量,初始化 obj 指向的 MyStack 结构体中的队列 q1 和 q2如果内存分配和初始化成功,最后,函数返回指向新创建的MyStack对象的指针obj

代码实现:

MyStack* myStackCreate() {
    MyStack *obj=(MyStack*)malloc(sizeof(MyStack));

    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL; 
    }
   //用于初始化 obj 指向的 MyStack 结构体中的队列 q1 和 q2
    QueueInit(&obj->q1);//初始化q1队列
    QueueInit(&obj->q2);//初始化q2队列

    //返回指向已创建并初始化的 MyStack 结构体对象的指针 obj
    return obj;
}

3.将元素压入栈顶

将元素推入栈时:

  1. 如果队列 q1 不为空,就将元素直接推入 q1 中;
  2. 如果 q1 为空,就将元素推入 q2 中。

        这样的实现方式可以确保在栈中的元素都集中在一个队列中(要么是 q1,要是 q2),而另一个队列则保持为空。这种实现方式的优势在于在执行出栈操作时,可以将非空队列中的元素转移到另一个空队列中,以便实现栈的先进后出特性。  

        

void myStackPush(MyStack* obj, int x) {
	if (!QueueEmpty(&obj->q1))// 如果 q1 不为空,将元素推入 q1
	{
		QueuePush(&obj->q1, x);
	}
	else// 如果 q1 为空,将元素推入 q2
	{
		QueuePush(&obj->q2, x);
	}
}

4.移除并返回栈顶元素

演示一遍出栈过程 

演示全程出栈过程(只演示栈内部的队列)

代码实现: 

int myStackPop(MyStack* obj)//栈出元素
 {
    //假设q1队列是空队列,q2非空队列
    Queue* pEmptyQ=&obj->q1;
    Queue* pNonEmptyQ=&obj->q2;
    if(!QueueEmpty(&obj->q1))//如果q1不为空
    {
			//此时空队列指针指向q2
      pEmptyQ=&obj->q2;
      pNonEmptyQ=&obj->q1;//非空队列指针指向q1
    }
    //挪数据,结束条件是非空队列的元素个数为1
    while(QueueSize(pNonEmptyQ)>1)
    {
        QueuePush(pEmptyQ,QueueFront(pNonEmptyQ));//获取非队列头部元素,把非空队列的元素入到空队列,
        QueuePop(pNonEmptyQ);//接着非空队列出元素
    }
		//此时非空队列只有一个元素
    int top=QueueFront(pNonEmptyQ);//获取此时非空队列的头部元素
    QueuePop(pNonEmptyQ);//到这步,非空队列出完所有元素
	 // 返非空队列元素
    return top;
}

5.返回栈顶元素

        q1队列如果不为空,那么返回q1队列的队尾元素当作自定义栈的栈顶元素,反之q2亦然。

int myStackTop(MyStack* obj)//返回栈顶元素
{
    if(!QueueEmpty(&obj->q1))//q1队列如果不为NULL
    {
        return QueueBack(&obj->q1);//返回q1队尾元素
    }
    else//q2队列如果不为NULL
    {
        return QueueBack(&obj->q2);//返回q2队尾元素
    }
}

6.判断栈是否为空栈

        判空就是要判断q1队列和q2队列都为空,才证明这个栈为空栈。

bool myStackEmpty(MyStack* obj)//判断自定义栈是否为空栈
{
    return QueueEmpty(&obj->q1) &&
    QueueEmpty(&obj->q2);
}

7.栈的销毁

错误写法:内存泄露

正确写法:

        先把q1和q2队列指向的节点free掉,接着再free掉整个MyStack,这样确保了不会内存泄露。

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

    free(obj);
}

自定义栈的代码实现 

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

MyStack* myStackCreate() {
    MyStack *obj=(MyStack*)malloc(sizeof(MyStack));
    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL; 
    }
    QueueInit(&obj->q1);//初始化q1队列
    QueueInit(&obj->q2);//初始化q2队列

    return obj;
}

// //注意以下写法
// typedef struct
// {
//     Queue* q1;
//     Queue* q2;
// }MyStack;

// MyStack* myStackCreate()
// {
//     MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
//     if(obj == NULL)
//     {
//         perror("malloc fail");
//         return NULL;
//     }
//     obj->q1=(Queue*)malloc(sizeof(Queue));
//     obj->q2=(Queue*)malloc(sizeof(Queue));

//     QueueInit(obj->q1);
//     QueueInit(obj->q2);
// 		return obj;
// }


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

int myStackPop(MyStack* obj)//栈出元素
 {
    //假设q1队列是空队列,q2非空队列
    Queue* pEmptyQ=&obj->q1;
    Queue* pNonEmptyQ=&obj->q2;
    if(!QueueEmpty(&obj->q1))//如果q1不为空
    {
			//此时空队列指针指向q2
      pEmptyQ=&obj->q2;
      pNonEmptyQ=&obj->q1;//非空队列指针指向q1
    }
    //挪数据,结束条件是非空队列的元素个数为1
    while(QueueSize(pNonEmptyQ)>1)
    {
        QueuePush(pEmptyQ,QueueFront(pNonEmptyQ));//获取非队列头部元素,把非空队列的元素入到空队列,
        QueuePop(pNonEmptyQ);//接着非空队列出元素
    }
		//此时非空队列只有一个元素
    int top=QueueFront(pNonEmptyQ);//获取此时非空队列的头部元素
    QueuePop(pNonEmptyQ);//到这步,非空队列出完所有元素
	 // 返非空队列元素
    return top;
}

int myStackTop(MyStack* obj)//返回栈顶元素
{
    if(!QueueEmpty(&obj->q1))//q1队列如果不为NULL
    {
        return QueueBack(&obj->q1);//返回q1队尾元素
    }
    else//q2队列如果不为NULL
    {
        return QueueBack(&obj->q2);//返回q2队尾元素
    }
}

bool myStackEmpty(MyStack* obj)//判断自定义栈是否为空栈
{
    return QueueEmpty(&obj->q1) &&
    QueueEmpty(&obj->q2);
}

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

    free(obj);
}

代码执行:

        到这里文章就结束了,如有错误欢迎指正,感谢你的来访与支持!

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

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

相关文章

Java毕业设计-基于SpingBoot的网上图书商城

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1. 简介2 技术栈3.1系统功能 4系统设计4.1数据库设计 5系统详细设计5.1系统功能模块5.1系统功能…

Python 图像处理库PIL ImageOps笔记

# 返回一个指定大小的裁剪过的图像。该图像被裁剪到指定的宽高比和尺寸。 # 变量size是要求的输出尺寸&#xff0c;以像素为单位&#xff0c;是一个&#xff08;宽&#xff0c;高&#xff09;元组 # bleed&#xff1a;允许用户去掉图像的边界&#xff08;图像四个边界&#xff…

APP产品经理的职责(合集)

APP产品经理的职责1 职责&#xff1a; 1、根据部门发展规划、主动发掘业务需求&#xff0c;独立负责线上用户产品线的完整业务规划、产品设计、产品管理等工作; 2、负责协调BD、运营、研发、市场等各部门&#xff0c;共同推进新产品开发&#xff0c;确保产品能够保质按时上线…

C语言指针,深度长文全面讲解

指针对于C来说太重要。然而&#xff0c;想要全面理解指针&#xff0c;除了要对C语言有熟练的掌握外&#xff0c;还要有计算机硬件以及操作系统等方方面面的基本知识。所以本文尽可能的通过一篇文章完全讲解指针。 为什么需要指针&#xff1f; 指针解决了一些编程中基本的问题。…

linus调试器---gdb的操作介绍

目录 一.背景 二.gdb的常用的操作介绍 小技巧&#xff1a;gdb会记住上一次的命令&#xff0c;按回车即可打出上次的命令。 1.看代码 2.打断点 3.删断点 4.禁用与开启断点 5.查看断点信息 6.调试 7.调试 8.查看变量 9.运行至某行 10.打印变量值 11.从一断点直接运行…

《深入浅出OCR》第一章:OCR技术导论

✨专栏介绍&#xff1a; 经过几个月的精心筹备&#xff0c;本作者推出全新系列《深入浅出OCR》专栏&#xff0c;对标最全OCR教程&#xff0c;具体章节如导图所示&#xff0c;将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 &#x1f468;‍&…

github 网页显示不全?

问题 解决 1、检查网页&#xff0c;打开 network&#xff0c;重新刷新 github 网页 2、查看无法加载的资源&#xff08;如 css 文件&#xff09; 3、查看域名地址 https://tool.chinaz.com/dns/&#xff0c;github.githubassets.com&#xff08;检查网页元素&#xff0c;点击无…

奥威BI系统,BI界便宜大碗国货

奥威BI系统&#xff08;软件&#xff09;是BI行业高性价比典范&#xff0c;不仅是因为“BI方案”极大地降低项目周期、实施成本&#xff0c;也是因为奥威BI系统具备先进的技术和架构和卓越的性能&#xff0c;有简单易用的特点&#xff0c;也提供了丰富的功能。 1、先进的技术和…

linux系统报“INFO: task java:xxx blocked for more than 120 seconds.”解决办法

1、问题描述 linux系统&#xff0c;输入dmesg -T&#xff0c;报“INFO: task java:xxx blocked for more than 120 seconds.”&#xff0c;如下 一般情况下&#xff0c;linux会把可用内存的40%的空间作为文件系统的缓存。当缓存快满时&#xff0c;文件系统将缓存中的数据整体同…

基于SSM的民宿管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

安达发|APS排单软件中甘特图的应用

近几年来&#xff0c;企业对生产效率和管理水平的要求越来越高。为了提高生产效率&#xff0c;降低生产成本&#xff0c;许多企业开始引入先进的生产计划与调度系统&#xff08;APS&#xff09;&#xff0c;实现生产过程的自动化、智能化管理。APS排产软件是一种能够根据企业的…

【JDK 8-集合框架】5.1 Stream 实战

一、什么是 stream 二、操作步骤 三、概览stream接口源码 四、实战 执行结果&#xff1a; 一、什么是 stream Stream 中文称为“流” 通过将集合转换为“流”的元素队列 通过声明性方式&#xff0c;能够对集合中的每个元素进行一系列并行或串行的流水线操作 元素是特定类…

为何消费者宁愿买iPhone14,也不买国产5G手机?差距太大了

随着iPhone15的上市&#xff0c;国产5G手机与苹果的较量也正式展开&#xff0c;让人意外的是不仅是iPhone15备受追捧&#xff0c;连带着旧款的iPhone14Pro也取得了销量的上涨&#xff0c;与国产5G手机形成了鲜明的对比。 一、iPhone的优势 苹果的A系处理器在性能方面遥遥领先毋…

C++简单模板介绍——template

一、泛型编程 概念&#xff1a; 在c语言中&#xff0c;同一种功能的函数&#xff0c;由于参数类型不同&#xff0c;虽然可以用函数重载&#xff0c;但可能需要重复多次的定义&#xff0c;又或者是不同类型的数据需要用到同一种数据结构时&#xff0c;需要定义大部分重复的类&…

thrift的简单使用

写在前面 本文一起看下一种由facebook出品的rpc框架thrift。 源码 。 1&#xff1a;开发步骤 1:编写thrift idl文件 2&#xff1a;根据thrift idl文件生成java模板代码 3&#xff1a;继承模板代码的*.Iface接口给出server的具体服务实现 4&#xff1a;使用模板的HelloWorldSe…

计算机毕设 LSTM的预测算法 - 股票预测 天气预测 房价预测

文章目录 0 简介1 基于 Keras 用 LSTM 网络做时间序列预测2 长短记忆网络3 LSTM 网络结构和原理3.1 LSTM核心思想3.2 遗忘门3.3 输入门3.4 输出门 4 基于LSTM的天气预测4.1 数据集4.2 预测示例 5 基于LSTM的股票价格预测5.1 数据集5.2 实现代码 6 lstm 预测航空旅客数目数据集预…

软件测试中最坏与一般健壮性测试用例区别

我们看下面一个问题 1.如果有一个n变量函数&#xff0c;健壮最坏情况测试会产生多少个测试用例 2.如果有一个n变量函数的被测程序&#xff0c;健壮性测试会产生多少个测试用例 首先我们知道一个变量的健壮测试有七个测试用例 即下图所示 1.问题一答案&#xff1a; 总的测试…

计算机竞赛 深度学习 python opencv 火焰检测识别

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…

TCP/IP客户端和服务器端建立通信过程

客户端和服务器端建立通信过程 客户端 connectToHost(const QString &, quint16 , QIODevice::OpenMode , QAbstractSocket::NetworkLayerProtocol )服务器端

Jmeter接口测试学习

1、设置CSV数据文件时&#xff0c;线程数设置为2或者多个&#xff0c;可以读取多行登录用户信息 2、循环&#xff0c;线程多次登录 3、http接口登录配置 4、CSV数据文件配置 数据文件&#xff1a; 5、如果需要鉴权(authorization)&#xff0c;如postman 下Basic Auth,可以设…