文章目录
- 前言
- 一、队列实现栈
-
- [1.2 代码的实现](#1.2 代码的实现)
-
- 1.2.1结构体的构建
- [1.2.2 初始化](#1.2.2 初始化)
- [1.2.3 模型入栈](#1.2.3 模型入栈)
- [1.2.4 模拟出栈并返回栈顶元素](#1.2.4 模拟出栈并返回栈顶元素)
- [1.2.5 获取栈顶元素](#1.2.5 获取栈顶元素)
- [1.2.6 判空](#1.2.6 判空)
- [1.2.7 释放](#1.2.7 释放)
- [二 栈实现队列](#二 栈实现队列)
-
- [2.1 思路分析](#2.1 思路分析)
- [2.2 代码实现](#2.2 代码实现)
-
- [2.2.1 结构体的构建](#2.2.1 结构体的构建)
- [2.2.2 初始化](#2.2.2 初始化)
- [2.2.3 将元素 x 推到队列的末尾(模拟入队)](#2.2.3 将元素 x 推到队列的末尾(模拟入队))
- [2.2.4 返回队列开头的元素](#2.2.4 返回队列开头的元素)
- [2.2.5 从队列的开头移除并返回元素(模拟出队)](#2.2.5 从队列的开头移除并返回元素(模拟出队))
- [2.2.6 判空](#2.2.6 判空)
- [2.2.7 销毁](#2.2.7 销毁)
前言
虽然在实践中没有什么意义,但是可以帮助我们熟悉和了解栈和队列
一、队列实现栈

思路 :
如下图所示,队列的规则是先进先出,而栈的规则是后进先出;如图中q1我们入队1、2、3、4;按照栈我们出栈的是4,因此我们将q1中的红色框中的数据导给q2,然后剩下4,4直接出队就达到了效果。

当我们出完4后,当我们想入数据,是给q1,还是q2呢?
q2,假设我们入给q1,如下图所示,然后我们需要6出队,该怎么出呢?是不是想将5导入到q2,再出6;那如果我再出5,你是不是要让q2的1、2、3、4导给q1,然后出5。如果再进行push呢,又给q2吗?这样会一直导来导去,逻辑会混乱。那栈顶的数据我们就不知道是在q1,还是在q2。因此,我们需要向非空的队列中去push数据。

1.2 代码的实现
首先需要了解队列的实现
c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataTpye;
typedef struct QueueNode
{
QDataTpye val;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* ptail;
QueueNode* phead;
int size;
}Queue;
//初始化
void QueueInit(Queue* pq);
//队尾插入
void QueuePush(Queue* pq, QDataTpye x);
//队头删除
void QueuePop(Queue* pq);
//取队头队尾的数据
QDataTpye QueueFront(Queue* pq);
QDataTpye QueueBack(Queue* pq);
//个数
int QueueSize(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//销毁
void QueueDestory(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataTpye x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("QueuePush::malloc");
return;
}
newnode->next = NULL;
newnode->val = x;
//空的
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
else//尾插
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->size != 0);
//一个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QueueNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
QDataTpye QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
QDataTpye QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = NULL;
cur = pq->phead;
while (cur)
{
QueueNode* tem = cur->next;
free(cur);
cur = tem;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
在实现队列的基础上再来用两个队列实现栈
1.2.1结构体的构建
首先我们需要封装一个结构体来管理这两个队列
c
typedef struct MyStack
{
Queue q1;
Queue q2;
}MyStack;
1.2.2 初始化
对结构体进行初始化
c
MyStack* MyStackCreat()
{
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
if (pst == NULL)
{
perror("MyStackCreat::malloc");
exit(1);
}
QueueInit(&(pst->q1));
QueueInit(&(pst->q2));
return pst;
}
1.2.3 模型入栈
谁不为空就入给谁,都为空随便入
c
void myStackPush(MyStack*obj, QDataTpye x)
{
if (!QueueEmpty(&(obj->q1)))
{
QueuePush(&(obj->q1), x);
}
else
{
QueuePush(&(obj->q2), x);
}
}
1.2.4 模拟出栈并返回栈顶元素
c
int myStackPop(MyStack* obj)
{
//假设法
MyStack* Empty = &(obj->q1);
MyStack* nonEmpty = &(obj->q2);
if (&(obj->q2) == NULL)
{
nonEmpty = &(obj->q1);
Empty = &(obj->q2);
}
//不为空前size-1导走,删除最后一个就是栈顶数据
while (QueueSize(nonEmpty) > 1)
{
QueuePush(Empty,QueueFront(nonEmpty));
QueuePop(nonEmpty);
}
int top = QueueFront(nonEmpty);
QueuePop(nonEmpty);
return top;
}
1.2.5 获取栈顶元素
找到不为空的队列返回队尾元素就是栈顶元素
c
int myStackTop(MyStack* obj)
{
if (!QueueEmpty(&(obj->q1)))
{
return QueueBack((&(obj->q1)));
}
else
{
return QueueBack((&(obj->q2)));
}
}
1.2.6 判空
也就是判断两个队列是否为空
c
bool myStckEmpty(MyStack* obj)
{
return QueueEmpty(&(obj->q2)) && QueueEmpty(&(obj->q1));
}
1.2.7 释放
直接释放obj没有,当我们直接释放掉obj时,除了释放掉了obj,后面的都没有释放掉,会造成内存泄漏

c
void myStackFree(MyStack* obj)
{
QueueDestory(&(obj->q1));
QueueDestory(&(obj->q2));
free(obj);
}
这里obj置不置空都行,它是一级指针,只起到传值的作用,改变不了实参。
二 栈实现队列

2.1 思路分析
首先我们需要明白栈是后进先出,而队列是先进先出。和上面队列实现栈一样,我们将一个不为空的栈导到另外一个栈去,然后出栈是不是就达到效果了。此时,第一个栈就变成空,如果我们再想入栈数据该怎么办呢?是向非空的栈入数据的话我们就需要保持之前的顺序,需要将第二个栈的数据导到第一个栈然后入栈数据,然后再将这个数据导入到第二个栈中出栈,这也是可以的,但是很麻烦。因此,我们只需要将一个栈作为入栈,另一个栈出栈就好了,比如之前导好后的数据,我们需要再入栈,就向最开始导的栈中入,然后等另外一个栈所有的数据完成出栈后,再将数据导入进去。

2.2 代码实现
首先我们需要在实现栈的基础上完成。
c
typedef int STDataType;
typedef struct
{
STDataType* arr; //指向栈数组空间的指针
int top; //栈顶位置
int capacity; //容量
}Stack;
//栈的初始化
void StackInit(Stack* s);
//栈的销毁
void StackDestory(Stack* s);
//核心逻辑
//x元素入栈
void StackPush(Stack* s, STDataType x);
//将栈顶元素出栈,并返回栈顶元素
STDataType StackPop(Stack* s);
//获取栈顶元素并返回
STDataType StackTop(Stack* s);
//获取栈中有效元素个数
int StackSize(Stack* s);
//检测栈是否为空,如果是空返回真,否则返回假
bool StackEmpty(Stack* s);
void StackInit(Stack* s)
{
assert(s);
s->arr = (STDataType*)malloc(4 * sizeof(STDataType));//开辟四个元素的空间
if (s->arr == NULL)
{
perror("StackInit::malloc");
return;
}
// 初始化时,top = 0,表⽰top指向的是栈顶元素的下⼀个位置
// 初始化时,top = -1,表⽰top指向的是栈顶元素
s->top = 0;
s->capacity = 0;
}
void StackPush(Stack* s, STDataType x)
{
assert(s);
//空间扩容
if (s->capacity == s->top)
{
int newcapacity = s->capacity == 0 ? 4 : 2 * s->capacity;
STDataType* tem = (STDataType*)realloc(s->arr, sizeof(STDataType) * newcapacity);
if (tem == NULL)
{
perror("tem::realloc");
exit(1);
}
s->arr = tem;
s->capacity = newcapacity;
}
//入栈
s->arr[s->top++] = x;
}
STDataType StackPop(Stack* s)
{
assert(s);
//判断是否为空,如为空就没有出栈的必要
if (!StackEmpty(s))
return s->arr[--s->top];
}
STDataType StackTop(Stack* s)
{
assert(s);
if (!StackEmpty(s))
return s->arr[s->top - 1];//top=0,指向的是栈顶下一个位置
}
int StackSize(Stack* s)
{
assert(s);
return s->top;
}
bool StackEmpty(Stack* s)
{
assert(s);
return s->top == 0;
}
void StackDestory(Stack*s)
{
assert(s);
if(s->arr)
{
free(s->arr);
s->arr = NULL;
s->top = 0;
s->capacity = 0;
}
}
2.2.1 结构体的构建
在栈的结构上构建两个栈,一个栈作为入栈,一个栈作为出栈
c
typedef struct STQ
{
Stack s1;//栈1入栈
Stack s2;//栈2出栈
}MyQueue;
2.2.2 初始化
c
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
if (obj == NULL)
{
perror("myQueueCreate::malloc");
exit(1);
}
StackInit(&(obj->s1));
StackInit(&(obj->s2));
return obj;
}
2.2.3 将元素 x 推到队列的末尾(模拟入队)
c
void myQueuePush(MyQueue* obj, int x)
{
assert(obj);
StackPush(&(obj->s1),x);
}
2.2.4 返回队列开头的元素
我们先确定s2是否为空,为空我们需要将s1的数据导入s2中,然后去s2的栈顶元素即可
c
int myQueuePeek(MyQueue* obj)
{
assert(obj);
if (StackEmpty(&(obj->s2)))
{
//导数据
while (!StackEmpty(&(obj->s1)))
{
int top = StackTop(&(obj->s1));
StackPush(&(obj->s2), top);
StackPop(&(obj->s1));
}
}
return StackTop(&(obj->s2));
}
2.2.5 从队列的开头移除并返回元素(模拟出队)
这个和上面的2.2.4差不多,都需要导入数据,但是导入数据后只需要pop就可以了。
c
int myQueuePop(MyQueue* obj)
{
assert(obj);
int top = myQueuePeek(&(obj->s2));
StackPop(&(obj->s2));
return top;
}
2.2.6 判空
c
bool myQueueEmpty(MyQueue* obj)
{
assert(obj);
return StackEmpty(&(obj->s1)) && StackEmpty (&(obj->s1));
}
2.2.7 销毁
c
void myQueueFree(MyQueue* obj)
{
StackDestory(&obj->s1);
StackDestory(&obj->s2);
free(obj);
}