文章目录
- 前言
- 一、用队列实现栈
- 二、用栈实现队列
- 三、设计循环队列
前言
本篇是围绕栈与队列来展开,需要知道一定有关它们的相关基础知识
栈的详解
队列的详解
还有一道基础的栈题——有效的括号
一、用队列实现栈
原题链接:用队列实现栈
解题思路:
首先创建一个队列结构
然后用两个队列去实现一个栈,每次始终保持一个队列为空
入栈操作相当于给非空队列进行入队操作
出栈操作相当于非空队列的队尾元素出队,此时需要把非空队列除最后一个元素之外的其余元素入队到空队列,然后出队最后一个队尾元素
//实现队列
typedef int QDateType;
typedef struct QueueNode
{
QDateType val;
struct QueueNode* next;
}QNode;
//管理队列的结构体
typedef struct Queue
{
QNode* phead;//指向队头的指针
QNode* ptail;//指向队尾的指针
int size;//队列数据个数
}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestroy(Queue* pq);
//队尾插入数据
void QueuePush(Queue* pq, QDateType x);
//队头删除数据
void QueuePop(Queue* pq);
//取出队尾的数据
QDateType QueueBack(Queue* pq);
//取出队头的数据
QDateType QueueFront(Queue* pq);
//取出队列数据个数
int QueueSize(Queue* pq);
//判断置空
bool QueueEmpty(Queue* pq);
//队列的初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//队列的销毁
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//队尾插入数据
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
//没有结点时,头插
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
//有结点时,尾插
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
//队头删除数据
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
//只有一个结点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//有多个结点
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
//取出队尾的数据
QDateType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
//取出队头的数据
QDateType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
//取出队列数据个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//判断置空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
typedef struct
{
Queue p1;
Queue p2;
} MyStack;
MyStack* myStackCreate()
{
//开辟一个模拟栈
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
//将队列初始化
QueueInit(&(pst->p1));
QueueInit(&(pst->p2));
return pst;
}
void myStackPush(MyStack* obj, int x)
{
//将数据插入非空队列
if(!QueueEmpty(&(obj->p1)))
{
QueuePush(&(obj->p1), x);
}
else
{
QueuePush(&(obj->p2), x);
}
}
int myStackPop(MyStack* obj)
{
//假设法
Queue* empty = &(obj->p1);
Queue* nonempty = &(obj->p2);
if(!QueueEmpty(&(obj->p1)))
{
empty = &(obj->p2);
nonempty = &(obj->p1);
}
//将size-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->p1)))
{
return QueueBack(&(obj->p1));
}
else
{
return QueueBack(&(obj->p2));
}
}
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&(obj->p1)) && QueueEmpty(&(obj->p2));
}
void myStackFree(MyStack* obj)
{
//将队列销毁
QueueDestroy(&(obj->p1));
QueueDestroy(&(obj->p2));
//将模拟栈销毁
free(obj);
}
二、用栈实现队列
原题链接:用栈实现队列
解题思路:
此题可以用两个栈实现,一个栈进行入队操作,另一个栈进行出队操作
出队操作: 当出队的栈不为空时,直接进行出栈操作,如果为空,需要把入队的栈元素全部导入到出队的栈,然后再进行出栈操作
//实现动态增长的栈
typedef int STDateType;
typedef struct Stack
{
STDateType* a;
int top;//栈顶
int capacity;//容量
}ST;
//初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);
//入栈和出栈
void STPush(ST* pst, STDateType x);
void STPop(ST* pst);
//取栈顶数据
STDateType STTop(ST* pst);
//判空
bool STEmpty(ST* pst);
//获得数据的个数
int STSize(ST* pst);
//初始化和销毁
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
//top指向栈顶数据的下一个元素
pst->top = 0;
//top指向栈顶元素
//pst->top = -1;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
//入栈和出栈
void STPush(ST* pst, STDateType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
//扩容
int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
STDateType* tmp = realloc(pst->a, newcapacity * sizeof(ST));
if (tmp == NULL)
{
perror("realloc 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--;
}
//取栈顶数据
STDateType 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* pst = (MyQueue*)malloc(sizeof(MyQueue));
//初始化栈
STInit(&(pst->pushst));
STInit(&(pst->popst));
return pst;
}
void myQueuePush(MyQueue* obj, int x)
{
//默认插入第一个栈
STPush(&(obj->pushst),x);
}
int myQueuePeek(MyQueue* obj);
int myQueuePop(MyQueue* obj)
{
//调用peek函数
int top = myQueuePeek(obj);
//删除栈顶元素
STPop(&(obj->popst));
return top;
}
int myQueuePeek(MyQueue* obj)
{
//检查第二个栈是否为空
if(STEmpty(&(obj->popst)))
{
//如果空,将第一个栈的数据移动过来
while(!STEmpty(&(obj->pushst)))
{
STPush(&(obj->popst), STTop(&(obj->pushst)));
STPop(&(obj->pushst));
}
}
//直接返回栈顶
return STTop(&(obj->popst));
}
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&(obj->pushst)) && STEmpty(&(obj->popst));
}
void myQueueFree(MyQueue* obj)
{
//将栈销毁
STDestroy(&(obj->pushst));
STDestroy(&(obj->popst));
//将模拟队列销毁
free(obj);
}
三、设计循环队列
原题链接:设计循环队列
解题思路:
通过一个定长数组实现循环队列
入队:首先要判断队列是否已满,再进行入队的操作,入队操作需要考虑索引循环的问题,当索引越界,需要让它变成最小值
出队:首先要判断队列是否为空,再进行出队操作,出队也需要考虑索引循环的问题
判空: 队头 = 队尾
判满: 队尾 + 1 = 队头
typedef struct
{
int* a;//动态数组储存数据
int head;//指向队头
int tail;//指向队列尾数据的下一个位置
int k;//队列长度
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k)
{
//开辟一个循环队列
MyCircularQueue* pst = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//创建一个可以存放k个元素的循环队列,实际申请的空间为k + 1
pst->a = (int*)malloc((k + 1) * sizeof(int));
pst->head = 0;
pst->tail = 0;
pst->k = k;
return pst;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->head == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->tail + 1) % (obj->k + 1) == obj->head;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj))
{
return false;
}
//注意回绕问题
obj->a[obj->tail++] = value;
obj->tail %= obj->k + 1;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return false;
}
//注意回绕问题
obj->head++;
obj->head %= obj->k + 1;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->a[obj->head];
}
int myCircularQueueRear(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->a[(obj->tail - 1 + obj->k + 1 )% (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj)
{
free(obj->a);
free(obj);
}