队列实现栈
用队列实现栈
在数据结构的世界里,栈和队列是两种基础且重要的数据结构。栈遵循后进先出原则,而队列遵循先进先出(FIFO)原则。那么,本文将带你实现队列对栈的实现。
下面为leetcode题目链接
队列实现栈
一、队列数据结构的基础定义与操作
(一)队列节点与队列结构体定义
c
typedef struct queuenode{
int data;
struct queuenode* next;
}queuenode;
typedef struct queue{
struct queuenode* phead;
struct queuenode* ptail;
}queue;
这里定义了两个结构体。queuenode 结构体用于表示队列中的节点,每个节点包含一个 data 成员用于存储数据,以及一个 next 指针用于指向下一个节点。queue 结构体则表示整个队列,包含两个指针 phead 和 ptail,分别指向队列的头部和尾部。
(二)队列大小计算函数
c
int queuesize(queue* pq)
{
if(pq == NULL)
return 0;
int count = 1;
queuenode* pcur = pq->phead;
while(pcur != pq->ptail)
{
pcur = pcur->next;
count++;
}
return count;
}
queuesize 函数用于计算队列的大小。首先检查传入的队列指针是否为空,如果为空则直接返回 0。然后通过遍历队列,从头部开始,直到当前节点等于尾部节点,每遍历一个节点,计数器 count 就加 1,最终返回队列的大小。需要注意的是,这里初始化 count 为 1,是因为如果队列不为空,至少有一个节点(即头部节点)。
(三)队列初始化函数
c
void queueinit(queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
}
queueinit 函数用于初始化队列。通过 assert 宏确保传入的队列指针不为空,然后将队列的头部指针 phead 和尾部指针 ptail 都初始化为 NULL,表示一个空队列。
(四)队列销毁函数
c
void queuedestroy(queue* pq)
{
queuenode* pcur = pq->phead;
while(pcur)
{
queuenode* next = pcur->next;
free(pcur);
pcur = next;
}
pq->phead = pq->ptail = NULL;
}
queuedestroy 函数用于销毁队列。首先从队列头部开始,依次释放每个节点的内存空间。通过一个临时指针 next 保存当前节点的下一个节点,然后释放当前节点,再将 pcur 指向下一个节点,直到遍历完整个队列。最后将队列的头部和尾部指针都置为 NULL,防止野指针。
(五)队列元素入队函数
c
void queuepush(queue* pq,int x)
{
queuenode* newnode = (queuenode*)malloc(sizeof(queuenode));
if(newnode == NULL)
return ;
newnode->data = x;
newnode->next = NULL;
if(pq->phead == NULL)
pq->phead = pq->ptail = newnode;
else
{ pq->ptail->next = newnode;
pq->ptail = newnode;
}
}
queuepush 函数用于将一个元素 x 加入到队列中。首先通过 malloc 分配一个新的节点空间,如果分配失败(即 newnode 为 NULL)则直接返回。然后将新节点的数据成员 data 赋值为 x,并将其 next 指针置为 NULL。如果当前队列为空(即 phead 为 NULL),则将新节点同时赋值给 phead 和 ptail;否则,将新节点链接到队列尾部,并更新 ptail 指针。
(六)队列是否为空判断函数
c
bool queueempty(queue* pq)
{
if(pq->phead == NULL)
return true;
else
return false;
}
queueempty 函数用于判断队列是否为空。只需检查队列的头部指针 phead 是否为 NULL,如果为 NULL 则表示队列为空,返回 true,否则返回 false。
(七)队列元素出队函数
c
void queuepop(queue* pq)
{
if(pq == NULL)
return;
if(pq->phead == pq->ptail)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
queuenode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
}
queuepop 函数用于从队列中移除头部元素。首先检查队列指针是否为空,如果为空则直接返回。如果队列中只有一个元素(即 phead 等于 ptail),则直接释放该节点,并将 phead 和 ptail 都置为 NULL。否则,保存头部节点的下一个节点指针,释放头部节点,然后将 phead 指向保存的下一个节点。
(八)获取队列头部元素函数
c
int queuefront(queue* pq)
{
if(pq->phead == NULL)
return -1;
return pq->phead->data;
}
queuefront 函数用于获取队列头部的元素。如果队列为空(即 phead 为 NULL),则返回一个特殊值(这里是 -1)表示错误。否则,返回 phead 所指向节点的 data 成员。
(九)获取队列尾部元素函数
c
int queueback(queue* pq)
{
if(pq->phead == NULL)
return -1;
return pq->ptail->data;
}
queueback 函数用于获取队列尾部的元素。同样,如果队列为空则返回 -1,否则返回 ptail 所指向节点的 data 成员。
二、用队列实现栈的具体实现
(一)栈结构体定义
c
typedef struct {
struct queue* q1;
struct queue* q2;
} MyStack;
这里定义了一个 MyStack 结构体,用于表示用队列实现的栈。它包含两个队列指针 q1 和 q2,通过这两个队列来模拟栈的操作。
(二)栈的创建函数
c
MyStack* myStackCreate() {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
pst->q1 = (queue*)malloc(sizeof(queue));
pst->q2 = (queue*)malloc(sizeof(queue));
queueinit(pst->q1);
queueinit(pst->q2);
return pst;
}
myStackCreate 函数用于创建一个新的栈。首先通过 malloc 分配 MyStack 结构体的内存空间,然后分别为 q1 和 q2 分配队列的内存空间,并调用 queueinit 函数初始化这两个队列,最后返回创建好的栈指针。
(三)栈元素入栈函数
c
void myStackPush(MyStack* obj, int x) {
if(!queueempty(obj->q1))
queuepush(obj->q1,x);
else
queuepush(obj->q2,x);
}
myStackPush 函数用于将一个元素 x 压入栈中。通过判断 q1 是否为空,如果 q1 不为空,则将元素加入到 q1 中;否则将元素加入到 q2 中。这样保证了在任何时刻,只有一个队列中有元素,另一个队列为空。
(四)栈元素出栈函数
c
int myStackPop(MyStack* obj) {
queue* emp = obj->q1;
queue* noneemp = obj->q2;
if(!queueempty(obj->q1))
{
emp = obj->q2;
noneemp = obj->q1;
}
while(queuesize(noneemp) > 1)
{
queuepush(emp,queuefront(noneemp));
queuepop(noneemp);
}
int top = queuefront(noneemp);
queuepop(noneemp);
return top;
}
myStackPop 函数用于弹出栈顶元素。首先确定哪个队列为空,哪个队列不为空(通过判断 q1 是否为空来交换 emp 和 noneemp 的指向)。然后将不为空的队列中除了最后一个元素之外的所有元素依次出队并加入到空队列中,这样最后一个元素就成为了栈顶元素。取出这个元素并返回,同时将其从队列中移除。
(五)获取栈顶元素函数
c
int myStackTop(MyStack* obj) {
queue* emp = obj->q1;
queue* noneemp = obj->q2;
if(!queueempty(obj->q1))
{
emp = obj->q2;
noneemp = obj->q1;
}
return noneemp->ptail->data;
}
myStackTop 函数用于获取栈顶元素。同样先确定不为空的队列,然后直接返回该队列尾部元素(因为在实现中,栈顶元素始终在不为空队列的尾部)。
(六)栈是否为空判断函数
c
bool myStackEmpty(MyStack* obj) {
return queueempty(obj->q1)&& queueempty(obj->q2);
}
myStackEmpty 函数用于判断栈是否为空。由于栈是通过两个队列实现的,所以当两个队列都为空时,栈为空,返回 true,否则返回 false。
(七)栈的销毁函数
c
void myStackFree(MyStack* obj) {
queuedestroy(obj->q1);
queuedestroy(obj->q2);
free(obj);
obj = NULL;
}
myStackFree 函数用于销毁栈。首先调用 queuedestroy 函数分别销毁 q1 和 q2 两个队列,然后释放 MyStack 结构体的内存空间,并将指针置为 NULL,防止野指针。