【数据结构】栈和队列
文章目录
1.栈(Stack)
1.1 栈的定义
栈是一种特殊的线性表,在其上进行DELETE操作所移除的元素是预先预定的,被删除的元素是最近插入的元素,实现的是一种后进先出(last-in,first-out,LIFO)策略
INSERT操作被称为压入、压栈(PUSH)
DELETE操作被称为弹出、出栈(POP)
我们称进行压栈与出栈的一端为栈顶,另一端称为栈底。
示意图:
这个名称很容易让我们联想到现实中的栈:比如餐厅里装有弹簧的摞盘子的栈。盘子从栈中弹出的次序正好同压入的次序相反。

(脑补一下竖着放的情形)
1.2 栈的实现
1.2.1对栈结构的分析设计:
经历了[上一节]((2条消息) 【数据结构】1.线性表___exile__的博客-CSDN博客)的学习,我们手上有两种工具来实现栈——数组(实现为数组栈)与链表(链式栈)
基于栈只能在一端进行插入删除的操作,相对而言,用数组来实现栈更优。因为数组在尾部插入删除效率高。(ps:用链表来实现栈时,最好在头部进行插入删除操作)
数组栈与链式栈示意图:
typedef int DataType;
typedef struct Stack
{
DataType* a;
int top; //栈顶后一位
int capacity; //栈容量
}Stack;
1.2.2 接口汇总
//初始化栈
void StackInit(Stack* pst);
//入栈
void StackPush(Stack* pst,DataType x);
//出栈
void StackPop(Stack* pst);
//获取栈顶元素
DataType StackTop(Stack* pst);
//获取栈中元素数目
int StackSize(Stack *pst);
//判断栈是否为空
int StackEmpty(Stack* pst);
//销毁栈
void StackDestroy(Stack* pst);
1.2.3 初始化栈
void StackInit(Stack* pst)
{
assert(pst);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
1.2.4 入栈
void StackPush(Stack* pst, DataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
DataType* tmp = (DataType*)realloc(pst->a, sizeof(DataType) * newcapacity);
if (tmp!=NULL)
{
pst->a = tmp;
pst->capacity = newcapacity;
}
else
{
printf("realloc failed\n");
exit(-1);
}
}
pst->a[pst->top++] = x;
}
1.2.5 出栈
void StackPop(Stack* pst)
{
assert(pst);
if (StackEmpty(pst)) return;
pst->top--;
}
1.2.6 获取栈顶元素
DataType StackTop(Stack* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
1.2.7获取栈中元素数目
int StackSize(Stack* pst)
{
assert(pst);
return pst->top;
}
1.2.8 判断栈是否为空
int StackEmpty(Stack* pst)
{
return pst->top == 0;
}
1.2.9 销毁栈
void StackDestroy(Stack* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->capacity = pst->top = 0;
}
2. 队列(Queue)
2.1 队列的定义
队列也是一种特殊的线性表,在其上进行DELETE操作所移除的元素是预先预定的,被删除的元素是在队列中存在时间最长的那个元素,实现的是一种先进先出(first-in,first-out,FIFO)策略
队列上的INSERT操作称为:入队(ENQUEUE)
DELETE操作称为:出队(DEQUEUE)
队列有队头(Front)和队尾(Rear),数据在==队尾入队,在队头出队==,同我们平时排队一样,很好理解。
队列示意图:
2.2 队列的实现
2.2.1 队列结构的设计分析
同队列一样,我们也有两种方式来实现队列——数组队列与链式队列
由于队列会频繁的在头部进行删删除操作,而数组在头删上效率低,因此用链表来实现一个队列更优。
typedef struct QueueNode
{
DataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* front;
QueueNode* rear;
}Queue;
2.2.2 接口汇总
//初始化队列
void QueueInit(Queue* pq);
//入队
void QueuePush(Queue* pq,DataType x);
//出队
void QueuePop(Queue* pq);
//获取队头元素
DataType QueueFront(Queue* pq);
//获取队尾元素
DataType QueueRear(Queue* pq);
//判断队列是否为空
int QueueEmpty(Queue* pq);
//获取队列长度
int QueueSize(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
2.2.3 初始化队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->front = pq->rear = NULL;
}
2.2.4 入队
void QueuePush(Queue* pq, DataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("malloc failed\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->rear == NULL)
{
pq->front = pq->rear = newnode;
}
else
{
pq->rear->next = newnode;
pq->rear = newnode;
}
}
2.2.5 出队
void QueuePop(Queue* pq)
{
assert(pq);
if (QueueEmpty(pq)) return;
QueueNode* top = pq->front;
//一个节点的情况
if (pq->rear == pq->front)
{
free(pq->front);
pq->front = pq->rear = NULL;
}
else
{
QueueNode* front = pq->front;
pq->front = pq->front->next;
free(front);
}
}
2.2.6 获取队头元素
DataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->front && pq->rear);
return pq->front->data;
}
2.2.7 获取队尾元素
DataType QueueRear(Queue* pq)
{
assert(pq);
assert(pq->front && pq->rear);
return pq->rear->data;
}
2.2.8 判断队列是否为空
int QueueEmpty(Queue* pq)
{
assert(pq);
return pq->front == NULL && pq->rear == NULL;
}
2.2.9 获取队列长度
int QueueSize(Queue* pq)
{
assert(pq);
int sz = 0;
QueueNode* front = pq->front;
while (front)
{
sz++;
front = front->next;
}
return sz;
}
2.2.10销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* front = pq->front;
while (front)
{
QueueNode* next = front->next;
free(front);
front = next;
}
pq->front = pq->rear = NULL;
}
3. 栈与队列的应用——互相实现
3.1 用队列来实现栈
leetcode题目地址:225. 用队列实现栈
3.1.1 结构设计
显然单一的队列不容易实现,我们在这用到两个队列。
typedef struct {
Queue q1;
Queue q2;
} MyStack;
3.1.2 实现思路(来回倒数据)
3.1.3 创建栈
MyStack* myStackCreate()
{
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
3.1.4 入栈
void myStackPush(MyStack* obj, int x)
{
Queue* emptyqueue=&obj->q1;
Queue* noemptyqueue=&obj->q2;
if(QueueEmpty(&obj->q2))
{
emptyqueue=&obj->q2;
noemptyqueue=&obj->q1;
}
QueuePush(noemptyqueue,x);
}
这边有个实现值得称道:
3.1.5 出栈
int myStackPop(MyStack* obj)
{
Queue* emptyqueue=&obj->q1;
Queue* noemptyqueue=&obj->q2;
if(QueueEmpty(&obj->q2))
{
emptyqueue=&obj->q2;
noemptyqueue=&obj->q1;
}
int size=QueueSize(noemptyqueue);
int ret=QueueRear(noemptyqueue);
for(int i=0;i<size-1;i++)
{
int cur=QueueFront(noemptyqueue);
QueuePop(noemptyqueue);
QueuePush(emptyqueue,cur);
}
QueuePop(nnemptyqueue);
return ret;
}
3.1.6 获取栈顶元素
栈顶元素即为非空队列队尾的元素
int myStackTop(MyStack* obj)
{
Queue* emptyqueue=&obj->q1;
Queue* noemptyqueue=&obj->q2;
if(QueueEmpty(&obj->q2))
{
emptyqueue=&obj->q2;
noemptyqueue=&obj->q1;
}
return QueueRear(noemptyqueue);
}
3.1.7 判断栈是否为空
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
3.1.8 销毁栈
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
3.2 用栈来实现队列
leetcode链接:232. 用栈实现队列
3.1.1 结构设计
同样我们需要两个栈
typedef struct {
Stack pushstack;
Stack popstack;
} MyQueue;
3.2.2 实现思路
-
设计上一个栈(pushstack)用来放push进来的数据,一个栈(popstack)用来存放要pop的数据。
-
入队简单,把数据push进pushstack中即可
-
出队时:我们根据定义,先在popstack中找数据,如果popstack空,那就把pushstack中的数据全部出栈再压入popstack中。
再把popstack中的栈顶数据pop出来并返回即可。
相较于上面的来回倒数据,这样的实现对于两个栈来说各司其职。
3.2.3 创建队列
MyQueue* myQueueCreate()
{
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->pushstack);
StackInit(&obj->popstack);
return obj;
}
3.2.4 入队
void myQueuePush(MyQueue* obj, int x)
{
StackPush(&obj->pushstack,x);
}
3.2.5 出队
int myQueuePop(MyQueue* obj)
{
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
int top=StackTop(&obj->pushstack);
StackPush(&obj->popstack,top);
StackPop(&obj->pushstack);
}
}
int top=StackTop(&obj->popstack);
StackPop(&obj->popstack);
return top;
}
3.2.6 获取队头元素
int myQueuePeek(MyQueue* obj)
{
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
int top=StackTop(&obj->pushstack);
StackPush(&obj->popstack,top);
StackPop(&obj->pushstack);
}
}
return StackTop(&obj->popstack);
}
3.2.7判断队列是否为空
bool myQueueEmpty(MyQueue* obj)
{
return StackEmpty(&obj->pushstack) &&
StackEmpty(&obj->popstack);
}
3.2.8 销毁队列
void myQueueFree(MyQueue* obj)
{
StackDestroy(&obj->pushstack);
StackDestroy(&obj->popstack);
free(obj);
}