目录
一、用栈实现队列(LeetCode第232题)
题目描述:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾;int pop()
从队列的开头移除并返回元素;int peek()
返回队列开头的元素;boolean empty()
如果队列为空,返回true
;否则,返回false。
思路分析:
- 题目提供两个栈结构,要求我们实现队列的先进先出功能;
- 栈的核心思路就是后进先出,而队列的核心是先进先出;
- 入队时,我们随便往一个的栈上存入数据,并将其命名为PUSH栈,专门负责入队操作;
- 出队时,我们需要把PUSH栈内数据往另一个没数据的栈转移,并将转移后得到的栈命名为POP栈,专门负责出队操作。
图解分析:
代码如下(栈的实现参考栈与队列)
typedef int STDataType;
typedef struct Stack
{
STDataType* a;//存放数据的数组
int top;//栈顶
int capacity;//栈的容量
}ST;
bool StackEmpty(ST* ps)//判断栈是否为空
{
return ps->top == 0;
}
void StackInit(ST* ps)//栈的初始化
{
assert(ps);//断言
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void StackDestroy(ST* ps)//栈的销毁
{
ps->capacity = 0;
ps->top = 0;
free(ps->a);
ps->a = NULL;
}
void StackPush(ST* ps, STDataType x)//入栈
{
assert(ps);
if (ps->capacity == ps->top)//是否需要增容
{
int newcapacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
STDataType* tmp = realloc(ps->a, sizeof(STDataType)*newcapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;//数据存入栈顶,再改变栈顶的位置
ps->top++;
}
void StackPop(ST* ps)//出栈,使栈顶元素消失
{
assert(ps);
assert(!StackEmpty(ps));//栈不为空才能出栈
ps->top--;
}
STDataType StackTop(ST* ps)//取栈顶元素,但不会让其消失
{
assert(ps);
assert(!StackEmpty(ps));//栈不为空才能出数据
return ps->a[ps->top - 1];
}
int StackSize(ST* ps)//计算栈内元素个数
{
assert(ps);
return ps->top;
}
typedef struct //匿名结构体,队列的基本结构
{
ST PushST;//入队的操作栈
ST PopST;//出队的操作栈
} MyQueue;
MyQueue* myQueueCreate() //队列的创建
{
MyQueue* Queue=(MyQueue*)malloc(sizeof(MyQueue));//开辟内存空间
StackInit(&Queue->PushST);//给两个栈初始化
StackInit(&Queue->PopST);
return Queue;//返回开辟的队列
}
void myQueuePush(MyQueue* obj, int x) //入队
{
StackPush(&obj->PushST,x);//调用栈的PUSH接口,往PUSH栈入数据
}
int myQueuePop(MyQueue* obj) //出队,并返回该元素
{
if(StackEmpty(&obj->PopST))//当Pop栈为空时,代表需要从PUSH栈获取元素
{
while(StackSize(&obj->PushST))//计算PUSH栈的元素个数,将其元素全部转移到POP栈
{
int tmp=StackTop(&obj->PushST);//得到PUSH栈栈顶元素
StackPush(&obj->PopST,tmp);//将该元素转移到POP栈内
StackPop(&obj->PushST);//在PUSH栈内移除该元素
}
}
int front=StackTop(&obj->PopST);//此时POP栈一定有元素,直接取栈顶元素保存
StackPop(&obj->PopST);//移除POP栈栈顶元素
return front;//返回该元素
}
int myQueuePeek(MyQueue* obj) {//与Pop函数类似,不多做注释
if(StackEmpty(&obj->PopST))
{
while(StackSize(&obj->PushST))
{
int tmp=StackTop(&obj->PushST);
StackPush(&obj->PopST,tmp);
StackPop(&obj->PushST);
}
}
int front=StackTop(&obj->PopST);
return front;
}
bool myQueueEmpty(MyQueue* obj) {//两个栈都为空才代表队列为空
return (StackEmpty(&obj->PushST)&&StackEmpty(&obj->PopST));
}
void myQueueFree(MyQueue* obj) {//先释放栈,再释放队列
StackDestroy(&obj->PushST);
StackDestroy(&obj->PopST);
free(obj);
}
二、用队列实现栈(LeetCode第225题)
题目描述:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 empty
)。
实现 MyStack
类:
void push(int x)
将元素 x 压入栈顶;int pop()
移除并返回栈顶元素;int top()
返回栈顶元素;boolean empty()
如果栈是空的,返回true
;否则,返回false
。
思路分析:
- 题目提供两个队列结构,要求我们实现栈的后进先出功能;
- 进栈时,我们需要往非空的队列进数据;
- 出栈时,因为我们需要的数据在非空队列的队尾,根据队列先进先出的特性,要先出到队尾的数据,则必须先将队头的前n-1个元素出队,所以我们需要借助另一个队列容器,将前n-1个数据按顺序依次存入队列2中,这样子就能得到将要出栈的数据了。
图解分析:
代码如下(队列的实现参考栈与队列)
typedef int QDataType;
typedef struct QueueNode//与单链表的单个结点结构一致
{
struct QueueNode* next;//指向下一结点
QDataType data;//存数据
}QueueNode;
typedef struct Queue//此结构体的作用是:因为需要时刻标记尾结点,所以将其封装成结构体更方便
{
QueueNode* head;//头结点
QueueNode* tail;//尾结点
}Queue;
bool QueueEmpty(Queue* pq)//队列判空
{
assert(pq);
return pq->head == NULL;
}
void QueueInit(Queue* pq)//初始化
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueueDestroy(Queue* pq)//销毁队列
{
assert(pq);
QueueNode* cur = pq->head;//遍历链表销毁结点,与单链表销毁一致
while (cur != NULL)
{
QueueNode* next = cur->next;//保存下一结点
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//入队
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//申请新结点
newnode->data = x;
newnode->next = NULL;
//从队尾插入新结点即可,需讨论第一次插入
if (pq->head == NULL)//head为空代表链表为第一次插入
{
pq->head = pq->tail = newnode;
}
else//链表不为空直接将其插入到tail的后面,然后改变tail即可
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)//出队
{
assert(pq);
assert(!QueueEmpty(pq));//队列不能为空,为空了就没东西出了
//从队头出数据,链表的头删操作即可
QueueNode* next = pq->head->next;//保存头结点的下一结点,防止释放头结点后找不到
free(pq->head);
pq->head = next;
if (pq->head == NULL)//如果head为空,证明队列已经无数据了,需要更新一下tail的指向,防止成为野指针
{
pq->tail = NULL;
}
}
QDataType QueueFront(Queue* pq)//取队头数据
{
assert(pq);
assert(!QueueEmpty(pq));//队列不能为空
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//取队尾数据
{
assert(pq);
assert(!QueueEmpty(pq));//队列不能为空
return pq->tail->data;
}
int QueueSize(Queue* pq)//计算队列元素个数
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;//遍历链表求个数
while (cur)
{
++n;
cur = cur->next;
}
return n;
}
typedef struct {//栈的结构,包含两个队列
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {//栈初始化
MyStack* ST=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&ST->q1);
QueueInit(&ST->q2);
return ST;
}
void myStackPush(MyStack* obj, int x) {//入栈,核心要求:往非空的队列入数据
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj) {//出栈
//找出空队列和非空队列
Queue* empty=&obj->q1;
Queue* nonempty=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
empty=&obj->q2;
nonempty=&obj->q1;
}
//将非空队列的前n-1个数据往空队列上转移,只留下最后一个数据
while(QueueSize(nonempty)>1)
{
QueuePush(empty,QueueFront(nonempty));
QueuePop(nonempty);
}
//取非空队列的最后一个元素出栈
int top=QueueFront(nonempty);
QueuePop(nonempty);
return top;
}
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);
}
void myStackFree(MyStack* obj) {//先释放队列,后释放栈
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}