一、本章重点
二、队列实现栈

我们有两个队列:

入栈数据1、 2、 3
可以将数据入队列至队列一或者队列二。

如何出栈?
但出栈要先出1,怎么办?
第一步:
将队列一出队n-1个至队列二。

第二步:
pop队列一的最后一个元素。

接下来怎么入栈呢?
将元素入队至不为空的队列。
怎么判断栈空?
队列一和队列二都为空的情况下,栈就是空的。
如何取栈顶元素?
取不为空的队列尾部元素。
总的来说就是,入栈时就将数据插入不为空的队列,出栈就将不为空的队列的前n-1个数据导入至另一个队列,然后pop最后一个元素。
代码实现:
首先我们要构造一个栈。
这个栈要包含两个队列
- typedef struct
- {
- Queue q1;
- Queue q2;
- } MyStack;
在此之前我们要准备好队列的一般接口:
我这里的队列是用单链表来构建的,具体接口实现可以看我之前的文章。
- typedef int QTypeData;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QTypeData val;
- }QN;
-
- void QueueInit(Queue* pq)//初始化队列
- size_t QueueSize(Queue* pq)//求队列元素个数
- int QueueBack(Queue* pq)//取队列尾部数据
- void QueuePush(Queue* pq, QTypeData x)//将x入队
- void QueuePop(Queue* pq)//出队
- void QueueDestroy(Queue* pq)//结束队列
我们要用队列实现栈的接口:
接口一:MyStack* myStackCreate()
这样做行吗?
- MyStack* myStackCreate()
- {
-
- MyStack ms;
- QueueInit(&ms.q1);
- QueueInit(&ms.q2);
- return ms;
- }
很显然,返回局部变量的地址是不明智的,因为在函数返回时,ms开辟的空间会重新交给操作系统,再次访问就是非法操作。
因此我们需要将ms开辟在堆区。
参考代码:
接口二:void myStackPush(MyStack* obj, int x)
入栈简单:只要将数据插入到不为空的队列即可。
入栈之前我们需要判断队列满吗?
不需要,因为我的队列是用单链表实现的,可以无限链接下去。
如果两个队列都为空,该插入到哪个队列呢?
我们可以这样做,如果队列1为空,就插入到队列2,如果不为空,就插入到队列1.
参考代码:
接口三:int myStackPop(MyStack* obj) //出栈
再次回顾一下我们是如何出栈的。
首先我们有两个队列
初始状态它们都是空的
队列一:空
队列二:空
入栈1、2、3、4
执行后
队列一:空
队列二:1、2、3、4
出队列只能先出1,如何出4呢?
把1、2、3先出给队列一
执行后
队列一:1、2、3
队列二:4
然后将4用变量记录并出队,最后返回记录4的变量。
执行后
队列一:1、2、3
队列二:空。
出队三板斧
第一步:即将不为空的队列的前n-1个元素入至为空的队列。
第二步:将剩下的1个元素用变量记录,然后将最后一个元素出队。
第三步:返回用变量记录的元素。
需要注意的是:如果栈为空,那么就返回false。
参考代码:
接口四:int myStackTop(MyStack* obj) //取栈顶元素
取栈顶元素之前我们需要保证栈不为空
如果栈为空,返回false。
取栈顶元素,即取不为空的队列的队尾元素。
参考代码:
接口五:bool myStackEmpty(MyStack* obj) //判断栈是否为空
如果两个队列都是空的那么该栈就是空的。
这里多提一下,栈的元素个数怎么求?
栈的元素个数就是那个不为空队列的元素个数。
参考代码:
接口六:void myStackFree(MyStack* obj) //结束栈
参考代码:
- void myStackFree(MyStack* obj)
- {
- QueueDestroy(&obj->q1);
- QueueDestroy(&obj->q2);
- free(obj);
- }
最后需要注意的是:调用栈为空的接口时,要先声明!!
三、栈实现队列

第一次入队

将数据1出队操作
将栈1的数据全导入栈2

然后栈2进行出栈操作

再次入队5、6、7
直接将5、6、7入栈至栈1

实际我们的对头和队尾是这样的

总的来说:
用两个栈实现一个队列,就得把一个栈专门入队,另一个栈用作出队。这里实现的时候我们用栈1做入队栈,栈2做出队栈。
也就是说,只要将数据入队,直接放入栈1.
出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.
队列的结构体:
- typedef struct
- {
- ST st1;
- ST st2;
- } MyQueue;
我们需要准备的栈
- typedef int STDataType;
- typedef struct Stack
- {
- STDataType* data;
- int top;
- int capacity;
- }ST;
这里我用的是动态数组实现的栈
需要提前准备栈的接口:
- void STInit(ST* p)
- void STDestroy(ST* p)
- void STPush(ST* p,STDataType x)
- void STPop(ST* p)
- bool STEmpty(ST* p)
- int STSize(ST* p)
- STDataType STRear(ST* p)
同样的,需要把队列开辟在堆区,同时对栈1和栈2进行初始化。
参考代码:
- MyQueue* myQueueCreate()
- {
- MyQueue* mq=(MyQueue*)malloc(sizeof(MyQueue));
- assert(mq);
- STInit(&mq->st1);
- STInit(&mq->st2);
- return mq;
- }
- void myQueuePush(MyQueue* obj, int x)
我们把栈1做入队栈,栈2做出队栈。
也就是说,只要将数据入队,直接放入栈1.
出队就直接出栈2的数据,如果栈2为空,这将栈1的数据全部导入栈2.
参考代码:
- void myQueuePush(MyQueue* obj, int x)
- {
- STPush(&obj->st1,x);
- }
- int myQueuePop(MyQueue* obj)
先要判断队列是否为空,如果队列为空则返回false。
其次如果栈2为空,就将栈1的数据全导入栈2.
参考代码:
- int myQueuePop(MyQueue* obj)
- {
- if(myQueueEmpty(obj))
- {
- return false;
- }
- if(STEmpty(&obj->st2))
- {
- int n=STSize(&obj->st1);
- while(n--)
- {
- STPush(&obj->st2,STRear(&obj->st1));
- STPop(&obj->st1);
- }
- }
- int ret=STRear(&obj->st2);
- STPop(&obj->st2);
- return ret;
- }
第四个接口:取队头元素
- int myQueuePeek(MyQueue* obj)
取队头元素之前,要判断队列是否为空,如果为空,则返回false
队头元素即栈2的尾部元素。

但如果栈2为空呢?
那队列的队头元素就是栈1的头部元素。
参考代码:
- int myQueuePeek(MyQueue* obj)
- {
- if(myQueueEmpty(obj))
- {
- return false;
- }
- if(STEmpty(&obj->st2))
- {
- return STFront(&obj->st1);
- }
- return STRear(&obj->st2);
- }
第五个接口:判断队列是否为空
- bool myQueueEmpty(MyQueue* obj)
队列为空,需要栈1和栈2都为空。
参考代码:
- bool myQueueEmpty(MyQueue* obj)
- {
- return STEmpty(&obj->st1) && STEmpty(&obj->st2);
- }
第六个接口:销毁队列
- void myQueueFree(MyQueue* obj)
参考代码:
- void myQueueFree(MyQueue* obj)
- {
- STDestroy(&obj->st1);
- STDestroy(&obj->st2);
- free(obj);
- }
注意:当使用判断队列是否为空的接口时,注意是否在之前声明过了。
四、解题思路总结
1.用队列实现栈:
我们需要用两个队列实现栈
栈类是于尾插尾删
队列是尾插头删
第一次入栈:两个队列都为空,随便插入一个队列都可
第一次出栈:出栈要出的是尾部数据,队列只能出头部数据,这是队列不能直接实现的。
那么需要将不为空的队列前n-1个数据导入至为空的队列,再将最后一个元素pop掉。
队列一:1、2、3、4
队列二:空
那么导入后
队列一:4
队列二:1、2、3
最后pop最后一个元素
队列一:空
队列二:1、2、3、4
再次尾插:尾插至不为空的队列即可。
2.用栈实现队列
我们有两个栈要求实现队列的一般接口
栈一:空
栈二:空
第一次入队:入栈至栈一或者栈二都可,这里选择栈一。
假设入队1、2、3、4
栈一:1、2、3、4
栈二:空
出队:要先出1
将栈一全部导入栈二
栈一:空
栈二:4、3、2、1
之后入队就插入至栈一
出队就pop栈二。

到此这篇关于C语言栈与队列相互实现详解的文章就介绍到这了,更多相关C语言 栈与队列内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!