首先要实现栈以及队列,而栈的实现在上一个博客(http://t.csdn.cn/fP2AI)内就已提及,在此处就不赘述了。
队列实现
由于队列具有先入先出,且只能在队尾入队队头出队的性质,用单向链表实现较为简单以及方便,用数组实现的话,不论是用数组头作队头还是数组尾,在入队或出队时都会比较麻烦,而且还需要考虑扩容;链表的话仅需要一个指针指向链表头结点,一个指针指向链表尾节点即可,入队就是在尾节点创建新节点,出队就是在对链表进行头删,这两种操作都较为简单。
实现代码:
头文件
#pragma
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps);//初始化栈
void STDestroy(ST* ps);//栈销毁
void STPush(ST* ps, STDataType x);//入栈
void STPop(ST* ps);//出栈
int STSize(ST* ps);//栈大小
bool STEmpty(ST* ps);//栈是否存在以及为空
STDataType STTop(ST* ps);//栈顶元素
对应函数
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STInit(ST* ps)
{
STDataType* a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (a == NULL)
{
perror("malloc fail");
return;
}
ps->a = a;
ps->capacity = 4;
ps->top = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* a = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (a == NULL)
{
perror("malloc fail");
return;
}
ps->a = a;
}
ps->a[ps->top] = x;
ps->top++;
}
void STPop(ST* ps)
{
assert(!STEmpty(ps));
ps->top--;
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
STDataType STTop(ST* ps)
{
assert(ps);
return ps->a[ps->top-1];
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
}
题解
先看看这道题(https://leetcode.cn/problems/implement-queue-using-stacks/)
用两个栈实现队列,我们已经知道,栈的性质是先入后出,队列的性质是先入先出,那么要怎么通过栈实现呢?
题目中要求了用两个栈来实现,就已经给出了按时了,通过观察不难发现,如下规律:
对于元素1,2,3按顺序入栈
由于栈始终只能取栈顶的元素,一个栈肯定是不行的,但是,有了两个栈就可以先拿一个栈专门用于接收数据,另一个专门出数据,只有在出队时专门用于出数据的栈才去专门用于入数据的栈拿取数据再出栈(注意要为空时才去取数据,不然就不会有队列的性质)。
入队顺序是1,2,3,出队顺序依然是1,2,3,符合了队列的性质。
代码附上
//调用了的函数在上面代码内都存在
//封装两个栈模拟队列
typedef struct {
ST PushSt;
ST PopSt;
} MyQueue;
//创建队列
//还是注意该函数是返回对应类型指针
//直接创建结构体会在函数结束使销毁
//因此用创建结构体指针+开辟空间的方式创建
MyQueue* myQueueCreate() {
MyQueue* STque = (MyQueue*)malloc(sizeof(MyQueue));
if(STque==NULL)
{
perror("malloc fail");
return NULL;
}
STInit(&STque->PushSt);
STInit(&STque->PopSt);
return STque;
}
//判断队列是否为空
//当两个栈都为空时,&&两边皆为true,即返回true,否则返回false
bool myQueueEmpty(MyQueue* obj) {
assert(obj);
return STEmpty(&obj->PushSt) && STEmpty(&obj->PopSt);
}
//入队,这个比较简单
void myQueuePush(MyQueue* obj, int x) {
assert(obj);
STPush(&obj->PushSt,x);
}
//查看队列的开头元素
//由于栈的性质原因需要进行一点操作
int myQueuePeek(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));
if(STEmpty(&obj->PopSt))
{
while(!STEmpty(&obj->PushSt))
{
STPush(&obj->PopSt,STTop(&obj->PushSt));
STPop(&obj->PushSt);
}
}
return STTop(&obj->PopSt);
}
//出队
//出队相比peek函数就多了一步Pop掉队列队头元素
int myQueuePop(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));
int ret = myQueuePeek(obj);
STPop(&obj->PopSt);
return ret;
}
//释放队列的空间
void myQueueFree(MyQueue* obj) {
assert(obj);
STDestroy(&obj->PushSt);
STDestroy(&obj->PopSt);
free(obj);
obj=NULL;
}
既然有栈模拟队列,那么也就有队列模拟栈了
如题(https://leetcode.cn/problems/implement-stack-using-queues/)
有了上一题铺垫就直接讲思路了。
大致思路就是先创建两个队列,第一次入队先随便选一个,后续入栈就调有元素的入栈,在出栈或者查看栈顶元素时就先把队列里的元素除最后一个以外存入另一个队列(由于队列先入先出的原因,相互倒腾元素始终还是按照最初入队的顺序,但是最后一个元素恰好是模拟栈的栈顶元素),然后就可以对最后一个元素进行出栈或者查看的操作。
附上代码
//前面的函数部分在上一篇博客就已经有了
//挑选下面的实现部分阅读即可
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType x;
}QNode;
//封装头尾指针以及大小,使传参更简便
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq);//队列初始化
void QueueDestroy(Queue* pq);//队列销毁
void QueuePush(Queue* pq, QDataType x);//入队
void QueuePop(Queue* pq);//出队
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//队列元素个数
QDataType QueueFront(Queue* pq);//队头元素
QDataType QueueBack(Queue* pq);//队尾元素
bool QueueEmpty(Queue* pq)//判断队列是否为空
{
assert(pq);
return pq->size == 0;
}
void QueueInit(Queue* pq)//队列初始化
{
QNode* tmp = (QNode*)malloc(sizeof(QNode));
if (tmp == NULL)
{
perror("malloc fail");
return;
}
pq->head = pq->tail = tmp;
pq->tail->next = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)//链表释放内存要一个一个节点释放
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
if(pq->size != 0)
{
QNode* tmp = (QNode*)malloc(sizeof(QNode));
if (tmp == NULL)
{
perror("malloc fail");
return;
}
pq->tail->next = tmp;
pq->tail = tmp;
pq->tail->next = NULL;
}
pq->tail->x = x;
pq->size++;
}
void QueuePop(Queue* pq)//出队
{
assert(pq);
assert(!QueueEmpty(pq));
QNode* tmp = pq->head->next;
if (pq->size == 1)
{
pq->size--;
}
else
{
free(pq->head);
pq->head = tmp;
pq->size--;
}
}
int QueueSize(Queue* pq)//队列元素个数
{
assert(pq);
return pq->size;
}
QDataType QueueFront(Queue* pq)//队头元素
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->x;
}
QDataType QueueBack(Queue* pq)//队尾元素
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->x;
}
//封装两个队列
typedef struct {
Queue que1;
Queue que2;
} MyStack;
//判断模拟栈是否为空
//注意顺序,没有声名函数就要把该函数置于需要使用该函数的上方
bool myStackEmpty(MyStack* obj) {
assert(obj);
//由于两队列只有一个会有元素,因而当二者都为空时,栈为空
return QueueEmpty(&obj->que1) && QueueEmpty(&obj->que2);
}
//创建栈
MyStack* myStackCreate() {
//注意不能MyStack Qst,创建结构体会在函数结束后销毁
MyStack* Qst;
MyStack* tmp = (MyStack*)malloc(sizeof(MyStack));
if(tmp==NULL)
{
perror("malloc fail");
return NULL;
}
Qst = tmp;
//由于Qst指向的是结构体,该结构体下又存在结构体
//要想修改原本结构体需要进行取地址,否则会出现形参实参的临时拷贝的基础问题
QueueInit(&Qst->que1);
QueueInit(&Qst->que2);
return Qst;
}
//入栈
void myStackPush(MyStack* obj, int x) {
assert(obj);
Queue* Que = &obj->que1;
//入栈始终往存在元素的队列入
if(QueueEmpty(Que))
{
Que = &obj->que2;
}
QueuePush(Que,x);
}
//出栈
int myStackPop(MyStack* obj) {
assert(obj);
myStackEmpty(obj);
Queue* NoneQue = &obj->que1;
Queue* Que = &obj->que2;
//出栈需要有元素的队列往无元素的队列入队
if(!QueueEmpty(NoneQue))
{
NoneQue = &obj->que2;
Que = &obj->que1;
}
//由于队列存在先入先出的特性,最后胜一个元素时,正好是最后入栈的元素
//再Pop掉从而实现栈的先入后出特性
while(Que->size>1)
{
QueuePush(NoneQue,QueueFront(Que));
QueuePop(Que);
}
int ret = QueueFront(Que);
QueuePop(Que);
return ret;
}
//返回栈顶元素
//栈顶元素正好是队尾元素,直接返回队尾元素即可
int myStackTop(MyStack* obj) {
assert(obj);
assert(!myStackEmpty(obj));
Queue* Que = &obj->que1;
if(QueueEmpty(Que))
{
Que = &obj->que2;
}
return QueueBack(Que);
}
//栈销毁
//由于栈是开辟了两个队列实现的,因而要对队列下的空间先进行free,再对开辟的队列free
//简言之就是malloc几次就free几次
void myStackFree(MyStack* obj) {
assert(obj);
QueueDestroy(&obj->que1);
QueueDestroy(&obj->que2);
}