🌹个人主页🌹:喜欢草莓熊的bear
🌹专栏🌹:数据结构
目录
2.2 MyQueue* myQueueCreate() //初始化
2.3 void myQueuePush(MyQueue* obj, int x)//插入数据
2.4 int myQueuePop(MyQueue* obj)//返回元素并且删除
2.5 int myQueuePeek(MyQueue* obj)//返回开头元素
2.6 bool myQueueEmpty(MyQueue* obj)//判空
2.7 void myQueueFree(MyQueue* obj)//销毁
前言
在上期博客中我们通过在队列里面进行数据倒换来成功的实现了栈,那我们试试能不能通过在两个栈里面倒换数据来实现队列呢?
一、题目
1.1题目链接:用栈实现队列
1.2 题目描述
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push
、pop
、peek
、empty
):实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 1, 1, false] 解释: MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1] myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2] myQueue.empty(); // return false提示:
1 <= x <= 9
- 最多调用
100
次push
、pop
、peek
和empty
- 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop
或者peek
操作)
1.3 题目分析
我们通过倒换两边栈的数据,就可以实现队列的特点" 先进先出 "。队列的其他功能在此基础上也比较好进行。
二、模拟实现“ 队列 ” 的功能
2.1 结构体的定义
根据之前的实现“ 栈 ”的经验我们可以重新定义一个结构体里面储存着两个栈就可以了,一个用来入数据一个用来出数据
typedef struct
{
ST pushst;
ST popst;
} MyQueue;
2.2 MyQueue* myQueueCreate() //初始化
我们动态申请一块空间,剩下的就是调用之前栈的初始化话说,和把size置为零。很简单和实现栈是一个道理。
MyQueue* myQueueCreate()
{
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
if (q == NULL)
{
perror("malloc");
}
STInit(&q->pushst);
STInit(&q->popst);
return q;
}
2.3 void myQueuePush(MyQueue* obj, int x)//插入数据
因为我们定义了两个栈,一个为入栈,一个为出栈。所以我们插入数据就只需插入到入数据的栈里面。
void myQueuePush(MyQueue* obj, int x)
{
//只管往pushst里插入即可,不需要管
STPush(&obj->pushst, x);
}
2.4 int myQueuePop(MyQueue* obj)//返回元素并且删除
我们这里分为两种情况,当出数据的栈为空时,和出数据的栈不为空。
出数据的栈为空时:那我们就要把入栈数据导入出数据的栈,然后进行删除。在删最后一个数据前把,最后一个数据储存后再删除返回刚刚储存的数据。
出数据的栈不为空:我们就直储存了数据,再进行释放数据。
int myQueuePop(MyQueue* obj)
{
//需要讨论下,当pop这个栈为空时,需要将push栈中的数据导过来
if (STEmpty(&obj->popst))
{
while (!STEmpty(&obj->pushst))//将push栈中的所有数据都导过去即可
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
//走到这里有两种情况,可能push栈里的数据导光了,也可能是pop栈里本来就有数据,不为空
int top = STTop(&obj->popst);
STPop(&obj->popst);
return top;
}
2.5 int myQueuePeek(MyQueue* obj)//返回开头元素
这边的返回开头元素和上一个删除并且返回元素十分相似,我们直接按照之前的思路写就可以了。
int myQueuePeek(MyQueue* obj)
{
//这里跟pop数据很像,直接return 栈顶元素即可
//需要讨论下,当pop这个栈为空时,需要将push栈中的数据导过来
if (STEmpty(&obj->popst))
{
while (!STEmpty(&obj->pushst))//将push栈中的所有数据都导过去即可
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
//走到这里有两种情况,可能push栈里的数据导光了,也可能是pop栈里本来就有数据,不为空
return STTop(&obj->popst);
}
2.6 bool myQueueEmpty(MyQueue* obj)//判空
很简单直接调用我们之前写栈的判空
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}
2.7 void myQueueFree(MyQueue* obj)//销毁
因为我们创建了两个栈,所以要先释放了两个栈的空间,再来释放把两个栈储存起来的结构体。
void myQueueFree(MyQueue* obj)
{
STDestory(&obj->pushst);
STDestory(&obj->popst);
free(obj);
}
三、代码展示
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>//bool 类型的头文件
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;//这里的top和顺序表的size相似的都是指有效元素的下一个数据。
int capacity;
}ST;
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
void STDestory(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity;
}
//入栈和出栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
//扩容
if (pst->top == pst->capacity)
{
int Newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, Newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("ralloc fail");
return;
}
pst->a = tmp;
pst->capacity = Newcapacity;
}
pst->a[pst->top++] = x;
}
void STPop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
pst->top--;
}
//获取栈数据
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
//栈的判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
//栈的数据个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
typedef struct
{
ST pushst;
ST popst;
} MyQueue;
MyQueue* myQueueCreate()
{
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
if (q == NULL)
{
perror("malloc");
}
STInit(&q->pushst);
STInit(&q->popst);
return q;
}
void myQueuePush(MyQueue* obj, int x)
{
//只管往pushst里插入即可,不需要管
STPush(&obj->pushst, x);
}
int myQueuePop(MyQueue* obj)
{
//需要讨论下,当pop这个栈为空时,需要将push栈中的数据导过来
if (STEmpty(&obj->popst))
{
while (!STEmpty(&obj->pushst))//将push栈中的所有数据都导过去即可
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
//走到这里有两种情况,可能push栈里的数据导光了,也可能是pop栈里本来就有数据,不为空
int top = STTop(&obj->popst);
STPop(&obj->popst);
return top;
}
int myQueuePeek(MyQueue* obj)
{
//这里跟pop数据很像,直接return 栈顶元素即可
//需要讨论下,当pop这个栈为空时,需要将push栈中的数据导过来
if (STEmpty(&obj->popst))
{
while (!STEmpty(&obj->pushst))//将push栈中的所有数据都导过去即可
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
//走到这里有两种情况,可能push栈里的数据导光了,也可能是pop栈里本来就有数据,不为空
return STTop(&obj->popst);
}
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}
void myQueueFree(MyQueue* obj)
{
STDestory(&obj->pushst);
STDestory(&obj->popst);
free(obj);
}
总结
我们完成栈和队列的互相实现,我们对栈和队列的理解就加深了一点。后面也会在带来一些关于队列和栈的题目,从而进一步加深。