片头
嗨! 小伙伴们,大家好! 今天我们一起来看看这道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题---用队列实现栈,里面包含了关于队列的基础知识和栈的基础知识,做完这道题能够帮助更好的理解栈和队列的性质,希望看完这篇文章能对友友们有所帮助 ! ! !
求点赞收藏加关注 ! ! !
谢谢大家 ! ! !