目录
一、两个栈实现一个队列
分析:栈是后进先出的,队列是先进先出的。如果要用两个栈实现一个队列,主要的两个接口就是实现入队和出队,所以我们使两个栈始终保持一个为入队栈,一个为出队栈,具体在下面分析。
1、定义
typedef struct QueueByTwoStack
{
Stack s1; //入队栈
Stack s2; //出队栈
}QueueByTwoStack;
2、入队 /出队
分析:
(1)入队:定义为 s1 为入队栈,则入队时,不用管两个栈是不是为空,直接入栈到s1。
(2)出队:出队就需要借助第二个栈了,从一开始,我们入栈的所有数据都在 s1 中,如果要实现队列的出队,就必须把 s1 中的数据倒到 s2 中,当所有数据倒过去的时候,我们会发现,s2的栈底是 s1 的栈顶,s2的栈顶是 s1 的栈底,所以实现出队,就是把 s2 的栈顶出栈,所以方法是:如果s2非空,直接出s2栈顶元素就好;如果s2为空,我们可以控制 s1 中元素的个数来达到出队,出队后,s2中剩余数据等待下一次出队即可。
void QueueByTwoStackPush(QueueByTwoStack* qts, DataType d)
{
assert(qts);
StackPush(&(qts->s1), d);
}
void QueueByTwoStackPop(QueueByTwoStack* qts)
{
assert(qts);
//1、如果s2有数据,则直接出
//2、如果s2没有数据,则将s1的数据倒过来,再出
if (StackEmpty(&(qts->s2)) == 0)
{
while (StackEmpty(&(qts->s1)))
{
StackPush(&(qts->s2), StackTop(&(qts->s1)));
StackPop(&(qts->s1));
}
}
StackPop(&(qts->s2));
}
3、其余接口
void QueueByTwoStackInit(QueueByTwoStack* qts)
{
StackInit(&(qts->s1));
StackInit(&(qts->s2));
}
void QueueByTwoStackDestory(QueueByTwoStack* qts)
{
StackDestory(&(qts->s1));
StackDestory(&(qts->s2));
}
DataType QueueByTwoStackFront(QueueByTwoStack* qts)
{
//S2是出队栈,s1为入队栈
//1、如果s2不为空,队头就相当于s2的栈顶
//2、如果s2为空,说明出队栈没有数据,此时队头在s1的栈底,为了得到栈底元素
//只需将s1倒到s2中,此时队头就是s2的栈顶
if (StackEmpty(&(qts->s2)) == 0)
{
while (StackEmpty(&(qts->s1)))
{
StackPush(&(qts->s2), StackTop(&(qts->s1)));
StackPop(&(qts->s1));
}
}
return StackTop(&(qts->s2));
}
int QueueByTwoStackSize(QueueByTwoStack* qts)
{
return StackSize(&(qts->s1)) + StackSize(&(qts->s2));
}
int QueueByTwoStackEmpty(QueueByTwoStack* qts)
{
return StackEmpty(&(qts->s1)) | StackEmpty(&(qts->s2));
}
void TestQueueByTwoStack()
{
QueueByTwoStack qts;
QueueByTwoStackInit(&qts);
QueueByTwoStackPush(&qts, 1);
QueueByTwoStackPush(&qts, 2);
QueueByTwoStackPush(&qts, 3);
QueueByTwoStackPush(&qts, 4);
QueueByTwoStackPush(&qts, 5);
while (QueueByTwoStackEmpty(&qts))
{
printf("%d ", QueueByTwoStackFront(&qts));
QueueByTwoStackPop(&qts);
}
printf("\n");
QueueByTwoStackDestory(&qts);
}
2、两个队列实现一个栈
分析:通过第一题,第二题就容易思考了,两个队列,我们需要做的就是始终保持其中一个为空。
1、定义
typedef struct StackByTwoQueue
{
Queue q1;
Queue q2;
}StackByTwoQueue;
2、入栈/出栈
分析:
(1)、入栈:入栈是每次向非空的队列中入数据。原因在于这是一个队列,我们只需要让数据入出的顺序保持一致就好,始终保持一个空队列是为了出栈做准备。
(2)、出栈:上面已经说了空队列是为了出栈做准备。原因在于,这是两个队列,无论我们怎么像第一题一样那样倒来倒去,我们所得到的结果都是一样的,队中存放的东西没有改变,只是换了一个队列存放罢了。但是,在倒的过程中,我们发现,非空队列中剩余一个数据的时候,这个数据就是我们想要出栈的数据,因此可以控制非空队列中的数据个数来找到我们想要出栈的数据。因此在出栈中定义两个队列,一个记录空队列,一个记录非空队列。
void StackByTwoQueuePush(StackByTwoQueue* stq, DataType d)
{
assert(stq);
//1、如果q1非空,就向q1入数据
//2、如果q1为空,则向q2入数据
if (QueueEmpty(&(stq->q1)) != 0)
{
QueuePush(&(stq->q1), d);
}
else
{
QueuePush(&(stq->q2), d);
}
}
void StackByTwoQueuePop(StackByTwoQueue* stq)
{
Queue* empty = NULL;
Queue* nonempty = NULL;
assert(stq);
empty = &(stq->q1); //指向空队列
nonempty = &(stq->q2); //指向非空队列
//1、如果q1不是空队列,说明记录错误,需要交换一下指向队列
if (QueueEmpty(&(stq->q1)) != 0)
{
empty = &(stq->q2);
nonempty = &(stq->q1);
}
//2、向空队列倒数据,直到非空队列中元素只有一个
// 即找到要出的数据
while (QueueSize(nonempty) > 1)
{
QueuePush(empty, QueueFront(nonempty));
QueuePop(nonempty);
}
//3、出数据
QueuePop(nonempty);
}
3、其他接口
void StackByTwoQueueInit(StackByTwoQueue* stq)
{
QueueInit(&(stq->q1));
QueueInit(&(stq->q2));
}
void StackByTwoQueueDestory(StackByTwoQueue* stq)
{
QueueDestory(&(stq->q1));
QueueDestory(&(stq->q2));
}
DataType StackByTwoQueueTop(StackByTwoQueue* stq)
{
//栈顶元素即队尾元素,只需找到非空队列
//返回非空队列的队尾元素
if (QueueEmpty(&(stq->q1)))
{
return QueueBack(&(stq->q1));
}
else
{
return QueueBack(&(stq->q2));
}
}
int StackByTwoQueueSize(StackByTwoQueue* stq)
{
return QueueSize(&(stq->q1)) + QueueSize(&(stq->q2));
}
int StackByTwoQueueEmpty(StackByTwoQueue* stq)
{
return QueueEmpty(&(stq->q1)) | QueueEmpty(&(stq->q2));
}
void TestStackByTwoQueue()
{
StackByTwoQueue stq;
StackByTwoQueueInit(&stq);
StackByTwoQueuePush(&stq, 1);
StackByTwoQueuePush(&stq, 2);
StackByTwoQueuePush(&stq, 3);
StackByTwoQueuePush(&stq, 4);
StackByTwoQueuePush(&stq, 5);
while (StackByTwoQueueEmpty(&stq))
{
printf("%d ", StackByTwoQueueTop(&stq));
StackByTwoQueuePop(&stq);
}
printf("\n");
StackByTwoQueueDestory(&stq);
}