本文算是对队列和栈的更深层次的理解,用他们自身的性质来完成相互间的转换,在此之前必须清楚的了解他们的实现以及应用。
目录
做题链接:225. 用队列实现栈 - 力扣(LeetCode) (leetcode-cn.com)
用队列实现栈
我们之前已经用单链表实现过队列,我们知道他的性质是先进先出,那怎么用两个队列来实现栈呢,用两个先进先出能否完成后进先出的栈。下面我们来完成第一个题:用队列实现栈
队列编写:
读题可知,我们必须先将队列定义完成,此题是在有队列的前提下完成的,前面已经实现过队列了,那我们直接完成队列的编写:
typedef int QDatatype;
//定义单链表结点
typedef struct QueueNode
{
QDatatype data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq,QDatatype x);
void QueuePop(Queue* pq);
bool QueueEmpty(Queue* pq);
size_t QueueSize(Queue* pq);
QDatatype QueueFront(Queue* pq);
QDatatype QueueBack(Queue* pq);
//队列初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
//队列销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
//队尾插入
void QueuePush(Queue* pq, QDatatype x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
assert(pq->tail==NULL);
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
//队头删除
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head&&pq->tail);
if (pq->head->next==NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
//判断是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL&&pq->tail == NULL;
}
//队列元素个数
size_t QueueSize(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
size_t size = 0;
while (cur)
{
size++;
cur = cur->next;
}
return size;
}
//对头元素
QDatatype QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
//队尾元素
QDatatype QueueBack(Queue* pq)
{
assert(pq);
assert(pq->tail);
return pq->tail->data;
}
栈的实现分析:
队列是先进先出,对头尾进行操作,那两个队列如何实现一个后进显出的栈呢?这样说大家还可能不知道这到底要干啥,简单来说:
我给队列中入队1 2 3 4 5,怎么样才能让他出队5 4 3 2 1,在两个队列中实现。
图解如下:
定义栈和栈的初始化
定义:栈由两个封装好的队列实现。
初始化:先给栈开辟空间,判断是否开辟成功,之后对栈内的两个队列进行初始化,用到我们定义队列时的初始化函数。
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* pst=(MyStack*)malloc(sizeof(MyStack));
assert(pst);
QueueInit(&pst->q1);
QueueInit(&pst->q2);
return pst;
}
元素入栈(入队)
对两个队列是否为空进行判断,将数据入队至非空队列,保留空队,方便数据来回导入。
void myStackPush(MyStack* obj, int x) {
assert(obj);
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
元素出栈(出队)
定义空和非空两个队列,每次对1 2队列进行判断赋给他们,方便后面的操作。
每次将非空队列的前Size-1个元素导入空队列中,删除非空队列的原数据,出队最后一个元素完成出栈。
注意细节:返回栈顶元素,栈顶即为非空队列的队尾元素,返回后再对非空队尾元素即栈顶进行出栈。
int myStackPop(MyStack* obj) {
assert(obj);
//判断空和非空是哪个
Queue* emptyQ=&obj->q1;
Queue* nonemptyQ=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
emptyQ=&obj->q2;
nonemptyQ=&obj->q1;
}
//把非空队列前N个数据导入空队列
//剩下一个删除实现后进先出
while(QueueSize(nonemptyQ)>1)
{
QueuePush(emptyQ,QueueFront(nonemptyQ));
QueuePop(nonemptyQ);
}
int top=QueueFront(nonemptyQ);
QueuePop(nonemptyQ);
return top;
}
返回栈顶元素
判断找出非空队列,直接反对非空队列队尾元素即可,队尾即栈顶。
int myStackTop(MyStack* obj) {
assert(obj);
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
判断栈是否为空和栈销毁
判断:栈有两个队列,两个队列都为空栈才为空,直接ruturn即可。
销毁:栈中两队列,两队列还指向两块空间,销毁时先销毁队列,防止出现内存泄漏问题。
bool myStackEmpty(MyStack* obj) {
assert(obj);
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
assert(obj);
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}