每日一题---OJ题: 用队列实现栈

片头

嗨! 小伙伴们,大家好! 今天我们一起来看看这道OJ题---用队列实现栈,话不多说,我们马上开始~

emmm,题目看似有点长,我们一起来分析分析!

我们都知道,的特点是后进先出(Last In First Out,简称 LIFO ),队列的特点是先进先出(First In First Out 简称 FIFO),明明两者的性质是相反的,怎么用队列实现栈呢?

屏幕前聪明的你应该已经知道了,哦! 一个队列肯定是实现不了的,那一个不行,两个队列总该可以吧!

嗯~聪明! 没错,的确是用两个队列实现,那么我们要怎样做呢?

思路一 :  ①保持一个队列存数据,一个队列为空 ②入数据,入到不为空的队列 ③出数据,借助空队列倒数据即可

首先,我们先定义队列的结构(这里我直接把代码拿出来)

队列的头文件 Queue.h

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

typedef int ElemType;
typedef struct QueueNode {		//队列的每一个结点
	ElemType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue {		   //队列
	QNode* front;			   //头结点
	QNode* rear;			   //尾结点
	int size;				   //队列的长度
}Queue;


//初始化队列
void QueueInit(Queue* q);
//队尾入队列
void QueuePush(Queue* q, ElemType x);
//队头出队列
void QueuePop(Queue* q);
//获取队列头部元素
ElemType QueueFront(Queue* q);
//获取队列队尾元素
ElemType QueueBack(Queue* q);
//获取队列中有效元素的个数
int QueueSize(Queue* q);
//检测队列是否为空
int QueueEmpty(Queue* q);
//销毁队列
void QueueDestroy(Queue* q);

定义队列的实现方法 Queue.c

#include"Queue.h"
//初始化队列
void QueueInit(Queue* q) {
	assert(q);
	q->front = NULL;
	q->rear = NULL;
	q->size = 0;
}
//队尾入队列
void QueuePush(Queue* q, ElemType x) {
	assert(q);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	newNode->data = x;
	newNode->next = NULL;

	if (q->front == NULL) {
		q->front = q->rear = newNode;
	}
	else {
		q->rear->next = newNode;
		q->rear = newNode;
	}
	q->size++;
}
//队头出队列
void QueuePop(Queue* q) {
	assert(q);
	//0个结点
	assert(q->front != NULL);
	//1个结点
	//多个结点
	if (q->front->next == NULL) {
		free(q->front);
		q->front = q->rear = NULL;
	}else{
		QNode* del = q->front;
		q->front = q->front->next;
		free(del);
		del = NULL;
	}
	q->size--;

}
//获取队列头部元素
ElemType QueueFront(Queue* q) {
	assert(q);
	assert(q->front != NULL);

	return q->front->data;
}
//获取队列队尾元素
ElemType QueueBack(Queue* q) {
	assert(q);
	assert(q->rear != NULL);

	return q->rear->data;
}
//获取队列中有效元素的个数
int QueueSize(Queue* q) {
	assert(q);
	return q->size;
}
//检测队列是否为空
int QueueEmpty(Queue* q) {
	assert(q);
	return q->size == 0;
}
//销毁队列
void QueueDestroy(Queue* q) {
	assert(q);
	QNode* p = q->front;
	while (p != NULL) {
		QNode* Q = p->next;
		free(p);
		p = Q;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}

OK,准备工作做好啦,接下来需要怎么做呢?

嗯,题目说,需要用两个队列实现栈,我们已经写出了队列的代码,接下来我们需要一一实现这些函数

别慌,咱们先来画个图哈~

①初始化2个队列(QueueInit)

假设这里有2个队列,分别为q1和q2,初始时均为空

②往其中一个空队列里插入元素(Push)

接着我们往其中一个队列(q1或者q2都可以)里面插入元素 1, 2, 3, 4

③删除元素(Pop)

然后我们想要删除最后一个元素,类似于栈里面的Pop,但是题目给的条件是队列,所以,如果我们用队列的方式Pop(删除)的话,只能删除队头的元素,也就是"1"

那怎么办呢? 很简单,不是有2个队列么,把非空队列的前 n-1 个元素放到另外一个队列(也就是空队列就好了呀)!

来看看例子:

第一次: 我们先获取q2里面的队头元素"1",把它拷贝放入q1中,再将这个值从q1中删除

第二次: 我们接着获取q2里面的队头元素"2",将它拷贝放入q1中, 再将这个值从q1中删除

第三次: 我们接着获取q2里面的队头元素"3",将它拷贝放入q1中, 再将这个值从q1删除

OK,现在q2队列里面只有"4",这一个元素了,相当于栈顶, 此时,我们只需要获取"4"这个元素,然后将它从q2队列里面删除就行了, 删除完"4"这个元素后,q2队列为空

④获取栈底元素(非空队列里面的最后一个元素)

诶,说到这里,如果我们想直接获取非空队列的最后一个元素(也就是栈顶元素),需不需要这么挪动元素呢?

答案是: 不需要啦! 还是上面这个例子,现在元素"4"已经被我们删除了,如果我们想从q1队列里面"1","2","3"元素中获取最后一个元素"3",相当于栈底元素,我们可以直接调用队列的QueueBack函数(获取队尾的元素),这样就不用挪动元素了。

⑤往非空队列里面继续插入新元素(Push)

那问题又来了, 如果我们此时想插入新元素"5",该怎么办呢?

哈哈哈,很简单,就直接把"5"插入到"3"的后面就可以了呀! 

道理我想都明白,那就是将新元素直接尾插到非空队列里面,方便下一次进行插入和删除。   

⑥判断栈(2个队列)是否为空

好啦,如何判断2个队列是否为空呢? 哈哈哈,so easy! 我们知道,当一个队列插入元素的时候,总有另外一个队列为空 ; 或者,当一个队列需要删除队尾元素的时候,总是需要将数据挪动到另外一个队列里面,然后获取这个队列的队尾元素,最后将这个元素删除,这个队列最后为空所以,不管怎么样,总有一个队列为空,所以,一个队列为空并不足以判断栈是否为空。只有当2个队列同时为空的时候,栈才为空。

⑦销毁栈

OK! 到这里,基本上关于栈的主要几个操作都实现了,接下来就是销毁操作咯! 毕竟,借了内存空间,就要归还嘛,免得内存泄漏,hhhhh~

怎么销毁由2个队列组成的栈呢? 很简单,就是将2个队列依次销毁,最后把我们在堆上malloc申请的MyStack结构体释放内存,归还给系统就可以啦!

欧克克~ 大功告成! 整道题的代码如下:

typedef int ElemType;
typedef struct QueueNode {		//队列的每一个结点
	ElemType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue {		   //队列
	QNode* front;			   //头结点
	QNode* rear;			   //尾结点
	int size;				   //队列的长度
}Queue;


//初始化队列
void QueueInit(Queue* q);
//队尾入队列
void QueuePush(Queue* q, ElemType x);
//队头出队列
void QueuePop(Queue* q);
//获取队列头部元素
ElemType QueueFront(Queue* q);
//获取队列队尾元素
ElemType QueueBack(Queue* q);
//获取队列中有效元素的个数
int QueueSize(Queue* q);
//检测队列是否为空
int QueueEmpty(Queue* q);
//销毁队列
void QueueDestroy(Queue* q);

//初始化队列
void QueueInit(Queue* q) {
	assert(q);
	q->front = NULL;
	q->rear = NULL;
	q->size = 0;
}
//队尾入队列
void QueuePush(Queue* q, ElemType x) {
	assert(q);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	newNode->data = x;
	newNode->next = NULL;

	if (q->front == NULL) {
		q->front = q->rear = newNode;
	}
	else {
		q->rear->next = newNode;
		q->rear = newNode;
	}
	q->size++;
}
//队头出队列
void QueuePop(Queue* q) {
	assert(q);
	//0个结点
	assert(q->front != NULL);
	//1个结点
	//多个结点
	if (q->front->next == NULL) {
		free(q->front);
		q->front = q->rear = NULL;
	}else{
		QNode* del = q->front;
		q->front = q->front->next;
		free(del);
		del = NULL;
	}
	q->size--;

}
//获取队列头部元素
ElemType QueueFront(Queue* q) {
	assert(q);
	assert(q->front != NULL);

	return q->front->data;
}
//获取队列队尾元素
ElemType QueueBack(Queue* q) {
	assert(q);
	assert(q->rear != NULL);

	return q->rear->data;
}
//获取队列中有效元素的个数
int QueueSize(Queue* q) {
	assert(q);
	return q->size;
}
//检测队列是否为空
int QueueEmpty(Queue* q) {
	assert(q);
	return q->size == 0;
}
//销毁队列
void QueueDestroy(Queue* q) {
	assert(q);
	QNode* p = q->front;
	while (p != NULL) {
		QNode* Q = p->next;
		free(p);
		p = Q;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}


typedef struct {
    //在 MyStack 结构体里面分别定义两个队列
    Queue q1;           
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    //在堆里面用mallo申请空间给2个队列
    MyStack* Q =(MyStack*) malloc(sizeof(MyStack));

    //将2个队列初始化
    QueueInit(&Q->q1);
    QueueInit(&Q->q2);

    //将结构体指针Q返回
    return Q;

}

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1)){      //若q1队列不为空,将新元素插入到q1
        QueuePush(&obj->q1,x);
    }else{                          //若q2不为空,将新元素插入到q2
        QueuePush(&obj->q2,x);
    }
}

int myStackPop(MyStack* obj) {
    Queue* pEmpy = &obj->q1;        //假设q1队列为空,q2队列不为空
    Queue* pNotEmpty = &obj->q2;    
    if(QueueEmpty(&obj->q2)){       //如果假设错误,那么q2队列为空,q1队列不为空
        pEmpy = &obj->q2;
        pNotEmpty = &obj->q1;
    }

    while(QueueSize(pNotEmpty) > 1){    //将非空队列的前n-1个元素倒入空队列
        int top = QueueFront(pNotEmpty);
        QueuePush(pEmpy,top);
        QueuePop(pNotEmpty);
    }
    int ret = QueueFront(pNotEmpty);
    QueuePop(pNotEmpty);
    return ret;                         //将非空队列里面的最后一个元素返回
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->q1)){      //如果q1队列不为空,将q1队列的最后一个元素返回
        return QueueBack(&obj->q1);
    }else{
        return QueueBack(&obj->q2); //如果q2队列不为空,将q2队列的最后一个元素返回
    }
}

bool myStackEmpty(MyStack* obj) {
     //2个队列同时为空,才能算空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);   
}

void myStackFree(MyStack* obj) {
    //将队列q1和队列q2都销毁
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    //将myStack销毁
    free(obj);
}

片尾

 今天我们学习了一道OJ题---用队列实现栈,里面包含了关于队列的基础知识和栈的基础知识,做完这道题能够帮助更好的理解栈和队列的性质,希望看完这篇文章能对友友们有所帮助 !   !   !

点赞收藏加关注 !   !   !

谢谢大家 !   !   !

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值