首页 > 其他分享 >使用两个队列实现栈

使用两个队列实现栈

时间:2023-06-02 21:01:56浏览次数:41  
标签:q1 head pq obj 队列 Queue 实现 使用


@TOC


前言

本文章主要介绍栈和队列的相互转换。


使用两个队列实现栈

我们知道,栈的特点是后进先出,而队列的特点是先进先出。

栈的特点:

使用两个队列实现栈_初始化

队列的特点:

使用两个队列实现栈_后进先出_02

使用两个队列实现栈的思路是:

1.向两个队列中的任一队列放入元素2.取出元素时,队列的功能是先进先出,要达到后进先出,需要将前面的所有元素取出,存入另一个空队列中,然后将剩下的最后一个元素释放掉即可。

比如:

使用两个队列实现栈_初始化_03

后面如果想继续插入元素的话,应该将插入的元素放在非空队列中,这样就实现了栈的功能。

使用两个队列实现栈_初始化_04

这样实现的原因是,两个队列中必有其中一个队列是空队列,保证了栈的后进先出的特点。

下面给出一道题目两个队列实现栈

使用两个队列实现栈_初始化_05

这里我用c语言来实现,所以先先写好队列的基本功能,再将接口函数引入。

1.队列接口函数引入

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

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	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);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = NULL;
	pq->tail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;

		free(del);
		//del = NULL;
	}

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = NULL;

	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;

		free(del);
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->head == NULL && pq->tail == NULL;
}

// 1G = 1024MB
// 1024MB = 1024*1024KB
// 1024*1024KB = 1024*1024*1024Byte

int QueueSize(Queue* pq)
{
	assert(pq);

	/*int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		++size;
	}

	return size;*/

	return pq->size;
}

2.栈的初始化

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

栈的初始化就是将两个队列进行初始化。

3.向栈中插入元素

保证其中一个队列不为空
//首先需要判断哪个队列为空,可以用ifelse来判断,但是这样操作冗余的,所以可以使用假设
int myStackPop(MyStack*
void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

由于无法得知哪一个队列为空,则需要判断或者假设,使用判断的化,代码比较冗余,在这里我使用假设,假设第一个队列是空。

4.出栈操作

int myStackPop(MyStack* obj) {
    Queue*EmptyQ = &obj->q1;
    Queue*NonEmptyQ = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        //如果q1不为空,返回假,!就返回真,进入if
        //意思就是我们假设错误了。
        EmptyQ = &obj->q2;
        NonEmptyQ = &obj->q1;
    }
    //来到这里已经知道哪个是空了,把所有元素导入非空队列,将最后一个不导
    while(QueueSize(NonEmptyQ)>1)
    {
        QueuePush(EmptyQ,QueueFront(NonEmptyQ));
        QueuePop(NonEmptyQ);
    }
    int top = QueueFront(NonEmptyQ);
    QueuePop(NonEmptyQ);
    return top;

}

5.取出栈顶元素

int myStackTop(MyStack* obj) {
    //取出栈顶元素
    //在首先的队列中,我们虽然不能删除队尾元素,但是我们可以取出队尾的元素
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

6.判断栈是否为空

bool myStackEmpty(MyStack* obj) 
{   
    //判断栈是否为空,需要判断两个队列是否为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

判断栈是否为空,需要判断两个队列是否为空

7.释放内存空间

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

注意,由于两个队列的维护是依靠指针,所以两个队列申请的空间先释放,再释放栈结构体空间


总结

本文章介绍了两个队列实现栈的方法。使用队列实现栈和使用栈实现队列都是可以的下篇文章介绍使用两个栈实现队列

标签:q1,head,pq,obj,队列,Queue,实现,使用
From: https://blog.51cto.com/u_15818575/6404978

相关文章

  • kali使用docker时遇到的错误及解决问题
    前言最近在学习在kali用docker搭建环境,但是一开始就遇到了问题本机无法访问kali开启的docker容器问题描述物理机访问kali开启的docker容器时访问不了。在虚拟机中可以通过telnetipport的方式可以确定docker容器的端口通过虚拟机可以访问,但是在物理机中无法通过telnet测试,并......
  • 使用hydra爆破SSH
    SSH爆破目录SSH爆破一、SSH是什么二、使用SSH的工具1、Xshell2、secureCRT3、WinSCP4、PuTTY5、MobaXterm6、FinalShell三、常见的端口对应服务四、模拟SSH爆破攻击1、信息收集2、爆破SSH3、SSH登录4、尝试创建隐藏计划任务!/bin/bash5、尝试nc连接目标主机的shell五、SSH爆......
  • python实现cookie登录
    前言之前有写过一个小程序,获取网站的回复(需要登陆)今天再去运行发现运行不了了再三检查后发现,是cookie没用了,可能是网站升级了吧重新获取一下cookie一、获取cookie1、用浏览器登录网站,以虎牙为例,按f12,选择Network,然后刷新网站2、找到最上面的huya.com,里面包含了cookie3、单机即......
  • 【React18专栏】React中monaco-editor组件的使用总结
    monaco-editor基础用法组件已经封装过了monaco-editor组件对json数据格式化的处理需求:在初始化加载json格式的数据时,需要实现monaco-editor组件对代码的自动格式化没有格式化的json格式数据显示如下:初始化加载数据完成后,想要达到的显示效果如下:界面上右键下边截图......
  • Java队列Disruptor 的使用
    、什么是Disruptor 从功能上来看,Disruptor是实现了“队列”的功能,而且是一个有界队列。那么它的应用场景自然就是“生产者-消费者”模型的应用场合了。可以拿JDK的BlockingQueue做一个简单对比,以便更好地认识Disruptor是什么。我们知道BlockingQueue是一个FIFO队列,生......
  • 2014.4.19.12.27_switch_8.28_java switch语句使用注意的四大细节_0.01
    javaswitch语句使用注意的四大细节很多朋友在使用javaswitch语句时,可能没有注意到一些细节,本文将详细介绍使用javaswitch语句四大要点,需要的朋友可以参考下。switch语句的格式如下:(它的功能是选出一段代码执行)switch(整数选择因子){case整数值1:语句;break;case整数值......
  • Sysmon 使用查询进程名称获取 DNS 查询日志==》看来早些版本是不支持溯源的!
    浏览器打开的域名: ss的请求:   svchost出去的也有:    系统更新,也是svchost发出去的:   ping的:    nslookup的,看不到:GG!!!    这是一个简单的“pinggoogle.com”命令,导致事件22记录在SysmonWindows事件日志中:它可以监视几乎任何支持网络的Windows客户端软件......
  • 定时器(JavaScript)的使用
    前言通过定时器自动的做一些事情,例如发送网络请求一、定时器定时器:定时器可以设定时间自动的做某件事情。定时器是一种方法,不是对象,定时器属于window对象。二、定时器具体内容周期性定时器:间隔一定的时间,自动的做某件事情setInterval(函数名,间隔时间)一次性定时器:延迟多长时间做......
  • 多环境简单使用,简单记录
    //------------多环境获取数组下面的值"DBS":[{"ConnId":"MYSQL1","Connection":"server=112.11.33.55\\ms2012;uid=sa;pwd=123;database=databaseqq;"},{"ConnId":"......
  • MAC/Razor页面应用如何使用微信认证
    @@openiddict微信二维码登入 ags:篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MAC/Razor页面应用如何使用微信认证相关的知识,希望对你有一定的参考价值。本文章演示了如何将微信集成到ABP应用程序中,使用户能够使用OAuth2.0凭据登录。创建一个沙箱账......