用队列实现栈
要求:仅使用两个队列去实现一个栈,并支持栈中的Push、Pop、Top、Empty四种操作
前言:我们知道栈和队列都是一种特殊的线性表,不同点在于栈只允许在一段进行插入和删除,遵循先进后出的原则;而队列允许在一端进行插入,在另一端进行删除,遵循先进先出的原则。
那么用队列去实现栈,无非就是用两个队列去实现一个先进后出的数据结构。
那么如何去实现这样的结构呢?
两个队列它肯定是有关系的,自然很容易就想到一个队列里面的数据进入到另一个队列当中
如图,假设一组数据3,4,5进入栈中,出栈必定是5,4,3的顺序,第一个出栈的元素是5。那么借用两个队列实现这种效果,最后呈现的结果也一定是5,4,3。
初始情况下,两个队列都为空,先让这组数据先进入到其中一个队列q1中,再让它们依次导入到另一个队列q2中。
按照队列的原则,依次导入的顺序为3,4,5.当我们只剩下最后一个数据5时,可以发现,此时如果将5直接出队列,而不去入q2,那么刚好符合栈的特性,先进先出,第一个出栈的元素是5。
由此可知,有n个数据在队列中,依次将前n-1个数据导入到另一个空队列,剩下最后一个数据直接出队列。再将n-1个数据按照此方法出最后一个数据,即可实现栈
具体实现:
已知是用队列的基本操作去实现栈的基本操作,所以在实现新栈之前,队列基本操作的相关代码要先写上(以下省略队列基本操作的代码,直接展示新栈的代码)
//入新栈
void mystackPush(Mystack* obj, int x)
{
//往空队列中插入数据
if (!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
//出新栈
int mystackPop(Mystack* obj)
{
//把已有数据的队列前size-1个元素,导入到空队列中
//先找出空队列
Queue* empQ = &obj->q1;//先假设q1是空队列
Queue* noneQ = &obj->q2;
if (!QueueEmpty(&obj->q1))//假设错误在调换
{
noneQ = &obj->q1;
empQ = &obj->q2;
}
//开始导入
while (QueueSize(noneQ) > 1)
{
int front = QueueFront(noneQ);
QueuePush(empQ, front);
QueuePop(noneQ);
}
//非空队列只剩下最后一个元素,即是要出栈的元素
int pop = QueueFront(noneQ);
QueuePop(noneQ);
return pop;
}
注意!!用两个队列实现一个栈操作的核心点就是,至少要有一个队列为空(这样才能保证可以倒导数据)
先创建两个变量empQ和noneQ,方便后面能够直接分清哪个队列为空哪个队列非空
//取新栈的栈顶元素
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);
}
思路总结:
必须保证至少有一个队列为空
出新栈:找不为空的队列,将size-1个数据导入到另一个空队列当中
入新栈:往不为空的队列中插入数据
取新栈顶元素:找不为空的队列取队尾元素
用栈实现队列
要求:仅使用两个栈去实现一个队列,并支持队列中的Push、Pop、Top、Empty四种操作
前言:同理,用栈去实现队列,无非就是用两个栈去实现一个先进先出的数据结构。
那么如何去实现这样的结构呢?
同上面的方法一致,也是利用数据从一个栈到另一个栈中实现队列的效果。
如图,假设一组数据6,7,8,9的顺序入队,出队的顺序必定是6,7,8,9。第一个出队的元素是6,借用个栈实现这种效果,最后呈现的结果也一定是6,7,8,9。
定义一个栈push专门实现入新队,另一个栈pop专门实现出新队(与新栈不同的是,因为提前定义了谁入谁出,就不需要去找空和非空)
由图易知,将push栈中的数据导入pop栈中,再由pop栈出,即可实现队列的特性
当出新队时,判断pop是否为空,若为空,直接将push里面的数据导入;若非空,就先出pop里面的数据
具体实现:
已知是用栈的基本操作去实现队列的基本操作,所以在实现新队列之前,栈基本操作的相关代码要先写上(以下省略栈基本操作的代码,直接展示新队的代码)
//入新队
void myqueuePush(Myqueue* obj, int x)
{
StackPush(&obj->pushST, x);
}
//出新队
int myqueuePop(Myqueue* obj)
{
//看popST是否为空,非空就先pop;为空就先将pushST的数据导入popST,再pop
if (StackEmpty(&obj->popST))
{
while (!StackEmpty(&obj->pushST))//导数据
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
//非空
int top = StackTop(&obj->popST);
StackPop(&obj->popST);
return top;
}
//取新队的队头元素
int myqueueTop(Myqueue* obj)
{
if (StackEmpty(&obj->popST))
{
while (!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
return StackTop(&obj->popST);
}
//判空
bool myqueueEmpty(Myqueue* obj)
{
return StackEmpty(&obj->pushST) && (StackEmpty(&obj->popST));
}
思路总结:
入新队:往pushST中插入数据
出新队:判断popST是否为空,不为空直接pop;为空就将pushST中的数据导入popST中
取新队的队头元素:跟出队一样,但是只取,不出