-
什么是栈 队列
-
如何仅用两个栈模拟实现队列
-
如何仅用两个队列模拟实现栈
-
循环队列
什么是栈 队列
栈 本质是一种线性表 ,秉持着 “后进先出”原则; 想象不出来的朋友可以类比 “弹匣”
队列 也是一种线性数据结构 它只允许一端进数据(队尾),另一端出数据(队头);
如何仅用两个栈模拟实现队列
在模拟实现过程中 对实现队列的 出列 是最麻烦的
我们将两个栈取名 分别是 pushstack popstack;
入列我们直接压栈pushstack即可 push 1 2 3 4
我们要pop出 1 ;而队列是 先进先出 栈是 后进先出;出列如何实现呢
我们可以用倒数据的方法
如图 即是popstack接收 再依次pop掉 非常方便
下面来看具体的代码实现
typedef struct {
//入队栈
Stack pushST;
//出队栈
Stack popST;
} MyQueue;
/** Initialize your data structure here. */
MyQueue* myQueueCreate(int maxSize) {
MyQueue* pqueue = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&pqueue->pushST, maxSize);
StackInit(&pqueue->popST, maxSize);
return pqueue;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
//入队栈进行入栈操作
StackPush(&obj->pushST, x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
//如果出队栈为空,导入入队栈的元素
if(StackEmpty(&obj->popST) == 0)
{
while(StackEmpty(&obj->pushST) != 0)
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
int front = StackTop(&obj->popST);
//出队栈进行出队操作
StackPop(&obj->popST);
return front;
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
//类似于出队操作
if(StackEmpty(&obj->popST) == 0)
{
while(StackEmpty(&obj->pushST) != 0)
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
return StackTop(&obj->popST);
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->pushST) == 0
&& StackEmpty(&obj->popST) == 0;
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->pushST);
StackDestroy(&obj->popST);
free(obj);
}
如何仅用两个队列模拟实现栈
用队列模拟实现栈 做法和上题类似 还是“倒数据” 但在具体实现中略有不同 会麻烦一些
push 到一个非空队列中即可 (两个都是空 任意即可)
pop功能 需要倒数据
将非空队列中的数据 依次插入到空队列中 只剩下需要pop掉的数据 再pop掉即可
此时 非空队列变成空队列 空队列变成非空队列了
后续要实现 push 和pop 即循环这几个步骤就行
下面我们来看下具体的代码实现
typedef struct {
Queue q1;
Queue q2;
} MyStack;
/** Initialize your data structure here. */
MyStack* myStackCreate(int maxSize) {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&pst->q1);
QueueInit(&pst->q2);
return pst;
}
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
//给非空队列进行入队操作
if(QueueEmpty(&obj->q1) != 0)
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
//把非空队列的除最后一个元素之外的剩余元素全部入队空队列
Queue* pEmpty = &obj->q1, *pNonEmpty = &obj->q2;
if(QueueEmpty(&obj->q1) != 0)
{
pEmpty = &obj->q2;
pNonEmpty = &obj->q1;
}
while(QueueSize(pNonEmpty) > 1)
{
QueuePush(pEmpty, QueueFront(pNonEmpty));
QueuePop(pNonEmpty);
}
int top = QueueFront(pNonEmpty);
//队尾元素出队
QueuePop(pNonEmpty);
return top;
}
/** Get the top element. */
int myStackTop(MyStack* obj) {
//获取非空队列的队尾元素
Queue* pEmpty = &obj->q1, *pNonEmpty = &obj->q2;
if(QueueEmpty(&obj->q1) != 0)
{
pEmpty = &obj->q2;
pNonEmpty = &obj->q1;
}
return QueueBack(pNonEmpty);
}
/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
return !(QueueEmpty(&obj->q1) | QueueEmpty(&obj->q2));
}
void myStackFree(MyStack* obj) {
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}
看到这里 我们已经学会了如何仅用两个栈来实现队列 和 如何仅用两个队列来实现栈
接下来 我们来学习一个 循环队列
循环队列 (惊喜 不看阳后悔)
什么是循环队列
一种队列 固定大小 首尾相连 可以利用队列之前用过的空间;一旦队列满了 我们就不能插入下一个元素
那我们应该用什么来实现循环队列呢 数组? 链表?
相信很多人第一眼就 觉得链表紫腚行 确实 但那是双向循环链表 而单链表行不行呢?
我们来分析下
1.如何判断空状态 和 满状态?
在这里我们使用 “添加一个空余节点”的方法,这样的话
可以 用front == rear 来判空
(rear + 1)%(k+1)== front 来判满
2.如何取队尾数据?
总结:单链表要实现这个就十分麻烦 所以我们使用数组
下面来看看代码实现
/*
解题思路:
通过一个定长数组实现循环队列
入队:首先要判断队列是否已满,再进行入队的操作,入队操作需要考虑索引循环的问题,当索引越界,需要让它变成最小值
出队:首先要判断队列是否为空,再进行出队操作,出队也需要考虑索引循环的问题
判空: 队头 == 队尾
判满: 队尾 + 1 == 队头
*/
typedef struct {
int* queue;
int front;
int rear;
int k
} MyCircularQueue;
/** Initialize your data structure here. Set the size of the queue to be k. */
//创建一个可以存放k个元素的循环队列,实际申请的空间为k + 1
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* pcq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pcq->queue = (int*)malloc(sizeof(int)*(k+1));
pcq->front = 0;
pcq->rear = 0;
pcq->k = k;
return pcq;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//判满
if((obj->rear+1)%(obj->k+1) == obj->front)
{
return false;
}
//队尾入队
obj->queue[obj->rear++] = value;
//如果队尾越界,更新为最小值
if(obj->rear == obj->k+1)
obj->rear = 0;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//判空
if(obj->front == obj->rear)
return false;
//队头出队
++obj->front;
//如果队头越界,更新为最小值
if(obj->front == obj->k+1)
obj->front = 0;
return true;
}
/** Get the front item from the queue. */
int myCircularQueueFront(MyCircularQueue* obj) {
if(obj->front == obj->rear)
return -1;
else
return obj->queue[obj->front];
}
/** Get the last item from the queue. */
int myCircularQueueRear(MyCircularQueue* obj) {
if(obj->front == obj->rear)
return -1;
//队尾元素再rear索引的前一个位置,如果rear为0,
//则队尾元素在数组的最后一个位置
if(obj->rear == 0)
return obj->queue[obj->k];
else
return obj->queue[obj->rear-1];
}
/** Checks whether the circular queue is empty or not. */
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->rear;
}
/** Checks whether the circular queue is full or not. */
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear+1)%(obj->k+1) == obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->queue);
free(obj);
}
循环队列学会了吗 还不快点一键三连 拜托拜托啦!!!