题目描述
之前博客已经详细介绍过了栈的实现,我们是直接动态内存开辟了一个数组来实现栈的先进后出的功能的,而这道题目是要求用两个队列来实现
题目中说了,用两个队列来实现栈,其实就是用两个先进先出的队列来实现栈的先进出后的功能
思路如下:
声明一下:本质上栈中存放是并不是两个队列,因为Queue q1和 Queue q2其实是两个包含了phead,ptail,size的结构体,而phead 和 ptail 才分别指向了 真正队列的头和尾,但是为了方便演示和大家直观感受,我们就直接认为栈中有两个队列
目前就有这么两个队列,现在要来实现题目中要求的栈的四种基本操作
①压栈
开始两个栈都为空,压栈随便放入一个队列即可,而且注意队列入数据是在队尾
当栈中已经有数据的时候,我们再入栈插入数据时肯定要把数据放入到有数据的队列当中
②出栈
栈的特点是先进的后出,后进的先出,因此我们现在需要弹出 4这个元素;
但目前1,2, 3,4都放在第一个队列当中,队列是先进先出,如果要直接出数据,那就只能出1;
但是别忘了,我们还有另外一个队列,因此我们这时需要把1,2,3这三个数据挪动到第二个队列,然后弹出4
③返回栈顶元素
栈顶元素对于对列来说,其实就是队尾元素,这就是我们当初为什么在实现队列功能时,出了有获取队头元素,还有获取队尾元素的操作
只需要判断一下哪个队列不为空,返回该队列的队尾元素即可~
④判断栈为空
由于栈是由两个队列来实现的,因此栈为空的特点是两个队列都为空
代码实现
队列的实现
栈的四种基本操作是由两个队列来实现的,因此我们先把之前讲过的队列的实现代码拿过来
#include<stdio.h>
#include<assert.h>
#include<stdbool.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 QueueDestory(Queue* pq);
//队列插入
void QueuePush(Queue* pq, QDataType x);
//出队头数据
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队列的尾
QDataType QueueBack(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);
//队列初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
//队列销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 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->phead == NULL)
{
assert(pq->ptail == 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));
//一个节点
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));
return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
//求队列长度
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
//return pq->phead == NULL && pq->ptail == NULL;
}
题目所给函数原型
要实现四种基本操作之前,肯定先要定义栈的结构以及创建栈,当然最后还有释放空间的过程
①定义栈的结构
其中包含了两个队列, q1和q2
typedef struct {
Queue q1;
Queue q2;
} MyStack;
②创建栈
1.我们注意到函数返回值是 MyStack* 类型的指针,因此肯定是要返回创建出栈的这块空间的起始地址。
2.创建栈的同时要对队列进行初始化操作,这样后续的操作才能进行
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
if(obj == NULL)
{
perror("malloc");
return NULL;
}
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);
}
}
上文已经提到了,如果栈没有数据,那么数据随便入到一个队列中
如果栈中已经由数据了,压栈需要把数据放入到非空的队列当中
那么需要分类讨论栈为不为空吗? 当然不需要
可以直接用if else 判断即可,当两个队列都为空时,把数据入到第二个队列即可
④出栈
第一个小问题:判断空与非空的队列 ~
上文已经提到,出栈需要捯数据从 非空队列 到 空队列当中,那么这里面需要用到规规矩矩的分类讨论空与非空吗?其实是不需要的!之前我有一篇博客讲解求相交链表的第一个节点时中涉及到长的链表的指针先走 |gap| 步的时候就提到过这个问题了~
我们直接假设第一个队列是非空的,第二个队列是空的,然后只需要if判断一下,如果假设正确,直接往下执行即可,如果假设错误,只需要更正一下即可~
第二个小问题:如何捯数据~, 什么时候就该停止了~
①如何捯? 捯数据肯定是一个一个捯,从 非空队列 捯数据 到 空队列 其实就是 获取非空队列的队头数据 从队尾入到空队列,然后弹出非空队列的队头数据
②什么时候停? 我们的初衷是要弹出栈顶元素,即非空队列的队尾数据,因此非空队列只剩下一个数据时,停止再捯~
③别忘了题目要求,返回栈顶元素~
下面详细演示出栈过程,假设目前第一个队列已经有1,2,3,4四个元素
⑤返回栈顶元素
上文提到返回栈顶元素其实就是返回非空队列的队尾元素~
⑥判断栈是否为空
栈为空意味着两个队列同时为空~
⑦销毁栈
除了应该释放掉动态内存开辟出的栈,还应该释放掉我们在底层使用的两个队列所占据的空间
开篇已经提到了,这道题的图本质应该这么画
因此free(obj)只是释放掉了左边的这块空间,并没有释放右边的两个队列所占据的空间,因此还要销毁一下两个队列~
完整代码
#include<stdio.h>
#include<assert.h>
#include<stdbool.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 QueueDestory(Queue* pq);
//队列插入
void QueuePush(Queue* pq, QDataType x);
//出队头数据
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队列的尾
QDataType QueueBack(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);
//队列初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
//队列销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 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->phead == NULL)
{
assert(pq->ptail == 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));
//一个节点
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));
return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
//求队列长度
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
//return pq->phead == NULL && pq->ptail == NULL;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
if(obj == NULL)
{
perror("malloc");
return NULL;
}
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) {
Queue* pEmptyQ = &obj->q1;
Queue* pNonEmptyQ = &obj->q2;
if(!QueueEmpty(pEmptyQ))
{
pEmptyQ = &obj->q2;
pNonEmptyQ = &obj->q1;
}
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))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)
&& QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}
本篇用队列实现栈就分享到这啦,欢迎大家交流指正~