题目
思路
创建两个栈,一个输入栈专门用来压入Push传入的数据,一个输出栈专门用来弹出pop和peek访问的栈顶数据。
增加数据入队时,就将数据全部压入输入栈;
弹出数据出队时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,等待弹出。这样输出栈从栈顶到栈底的元素顺序就是从队首到队尾的顺序。
代码实现
创建栈
声明一个栈数据类型,用于向操作系统申请内存和告诉程序员该存放什么数据。
//创建栈结构
typedef int STDataType;
typedef struct Stack
{
int top; //指向栈顶元素的下一个位置
int capacity;
STDataType* a; //动态数组
} ST;
值得注意,top定义为栈顶元素的下一个位置,因为top将从0开始,方便统计栈内数据个数。
实现栈
//实现栈的函数
//栈的初始化
void STInit(ST* pst);
//栈的填入数据
void STPush(ST* pst, STDataType x);
//栈的删除数据
void STPop(ST* pst);
//栈的判空
bool STEmpty(ST* pst);
//栈的栈顶元素访问
STDataType STTop(ST* pst);
//栈的大小计算
int STSize(ST* pst);
//栈的销毁
void STDestroy(ST* pst);
//实现栈的函数
//栈的初始化
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
//栈的填入数据
void STPush(ST* pst, STDataType x)
{
assert(pst);
//检查容量
if (pst->top == pst->capacity)
{
int newCapacity = pst->top == 0 ? 4 : pst->capacity * 2; //空栈申请4个元素,否则扩大一倍
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType)); //开辟新容量个空间
if (!tmp)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
//填入数据
pst->a[pst->top++] = x; //在top位置填入数据
}
//栈的判空
bool STEmpty(ST* pst)
{
return pst->top == 0; //比较运算符
}
//栈的删除数据
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
//栈的栈顶元素访问
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1]; //栈顶没存放数据
}
//栈的大小计算
int STSize(ST* pst)
{
assert(pst);
return pst->top; //top正好和元素个数对应
}
//栈的销毁
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
创建队列
创建队列的本质是定义一个变量来维护一块内存,需要说明使用哪些成员记录队列的信息。
创建一个变量,需要做两个工作:第一分配内存,第二插入相应的数据。
typedef struct
{
ST stackPush;
ST stackPop;
} MyQueue;
变量内存如下图所示:
创建队列:
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
if(!obj)
{
perror("malloc fail\n");
return NULL; //null实质就是一个地址
}
//初始化队列
STInit(&obj->stackPop);
STInit(&obj->stackPush);
return obj;
}
补充对NULL的理解:
A null-pointer constant is an integral constant expression that evaluates to zero (like 0
or 0L
), or the cast of such value to type void*
(like (void*)0
).
值:空指针常量是求值为零的整型常量表达式(如0或0L),或者将该值强制转换为类型void*(如(void*)0)。
A null pointer constant can be converted to any pointer type (or pointer-to-member type), which acquires a null pointer value. This is a special value that indicates that the pointer is not pointing to any object.
数据类型:NULL空指针常量可以转换为获取空指针值的任何指针类型(或指针到成员类型)。
指向位置:这是一个特殊值,表示指针没有指向任何对象。
插入元素
直接在插入栈中填入即可,不需要考虑边界情况
//将元素 x 推到队列的末尾
void myQueuePush(MyQueue* obj, int x)
{
assert(obj);
STPush(&obj->stackPush, x);
}
返回队头元素
实质是返回栈底元素。如果删除栈中有元素,就直接返回删除栈的栈顶元素,否则先倒入插入栈中的数据,再返回。
int myQueuePeek(MyQueue* obj)
{
assert(obj);
// assert(!myQueueEmpty(obj));
//如果stackPop为空,先倒入,再返回
if(STEmpty(&obj->stackPop))
{
//倒空stackPush
while(!STEmpty(&obj->stackPush))
{
// int top = STTop(&obj->stackPush);
STPush(&obj->stackPop, STTop(&obj->stackPush));
STPop(&obj->stackPush);
}
}
return STTop(&obj->stackPop);
}
移除并返回队头元素
先返回队头元素,再删除队头元素。
int myQueuePop(MyQueue* obj)
{
int top = myQueuePeek(obj); //保存队头元素
STPop(&obj->stackPop);
return top;
}
判空队列
如果队列为空,返回 true ;否则,返回 false
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->stackPush)
&& STEmpty(&obj->stackPop);
}
销毁队列
先销毁栈,再销毁队列。因为栈和队列分别指向一块堆内存,仅销毁栈队列,便会造成内存泄漏。
void myQueueFree(MyQueue* obj)
{
STDestroy(&obj->stackPop);
STDestroy(&obj->stackPush);
free(obj);
}
![](https://img-blog.csdnimg.cn/053b68dfa03f42a89e3059d1e88fe6ca.png)
完整代码
//创建栈结构
typedef int STDataType;
typedef struct Stack
{
int top; //指向栈顶元素的下一个位置
int capacity;
STDataType* a; //动态数组
} ST;
//实现栈的函数
//栈的初始化
void STInit(ST* pst);
//栈的填入数据
void STPush(ST* pst, STDataType x);
//栈的删除数据
void STPop(ST* pst);
//栈的判空
bool STEmpty(ST* pst);
//栈的栈顶元素访问
STDataType STTop(ST* pst);
//栈的大小计算
int STSize(ST* pst);
//栈的销毁
void STDestroy(ST* pst);
// #include "Stack.h"
//实现栈的函数
//栈的初始化
//内容:开辟动态数组、栈顶、容量
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
//栈的填入数据
void STPush(ST* pst, STDataType x)
{
assert(pst);
//检查容量
if (pst->top == pst->capacity)
{
int newCapacity = pst->top == 0 ? 4 : pst->capacity * 2; //空栈申请4个元素,否则扩大一倍
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType)); //开辟新容量个空间
if (!tmp)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
//填入数据
pst->a[pst->top++] = x; //在top位置填入数据
}
//栈的判空
bool STEmpty(ST* pst)
{
return pst->top == 0; //比较运算符
}
//栈的删除数据
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
//栈的栈顶元素访问
STDataType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1]; //栈顶没存放数据
}
//栈的大小计算
int STSize(ST* pst)
{
assert(pst);
return pst->top; //top正好和元素个数对应
}
//栈的销毁
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
//创建栈实现队列
//包含两个栈即可,不需要再做额外说明
typedef struct
{
ST stackPush;
ST stackPop;
} MyQueue;
//定义队列结构
//创建东西,就是分配内存,插入数据才意义
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
if(!obj)
{
perror("malloc fail\n");
return NULL; //null实质就是一个地址
}
//初始化队列
STInit(&obj->stackPop);
STInit(&obj->stackPush);
return obj;
}
//将元素 x 推到队列的末尾
void myQueuePush(MyQueue* obj, int x)
{
assert(obj);
// assert(!ST(obj));
STPush(&obj->stackPush, x);
}
//返回栈顶并删除
// int popTop(MyQueue* obj)
// {
// int top = STTop(&obj->stackPop);
// STPop(&obj->stackPop);
// return top;
// }
//返回队列开头的元素
int myQueuePeek(MyQueue* obj)
{
assert(obj);
// assert(!myQueueEmpty(obj));
//如果stackPop为空,先倒入,再返回
if(STEmpty(&obj->stackPop))
{
//倒空stackPush
while(!STEmpty(&obj->stackPush))
{
// int top = STTop(&obj->stackPush);
STPush(&obj->stackPop, STTop(&obj->stackPush));
STPop(&obj->stackPush);
}
}
return STTop(&obj->stackPop);
}
//从队列的开头移除并返回元素
int myQueuePop(MyQueue* obj)
{
int top = myQueuePeek(obj);
STPop(&obj->stackPop);
return top;
}
// 如果队列为空,返回 true ;否则,返回 false
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->stackPush) && STEmpty(&obj->stackPop);
}
//销毁内存
void myQueueFree(MyQueue* obj)
{
// free((&obj->stackPop)->a);
// free((&obj->stackPush)->a);
STDestroy(&obj->stackPop);
STDestroy(&obj->stackPush);
free(obj);
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
总结
-
实现一种数据结构需要说明内存的管理方式和数据的访问规则。
-
对于本题而言,队列元素的访问是遵循“先进先出”的规则,并且只能在队头取出数据,在队尾存入数据。
-
用栈实现意味着队列元素是存放在数组中,一块连续的内存中,并且还要遵守栈元素的访问规则。
-
队头元素在栈底,就只能使得队头元素成为栈顶元素才能在栈中访问。
-
创建一个变量,需要做两个工作:第一分配内存,第二插入相应的数据。