具备教学意义的实操(用队列实现栈)

225. 用队列实现栈 - 力扣(LeetCode)https://leetcode.cn/problems/implement-stack-using-queues/description/

实现逻辑

一个是先进先出(队列),一个是后进先出(栈)

这里用两个队列导入·一下数据

出来数据之后入数据

代码的实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{
	QNode* _phead;//头节点
	QNode* _ptail;//尾结点
	int size;
}Queue;
// 这里采取一级指针进行实现代码逻辑,如果不创建队列的结构,我们就需要采取二级指针
// 初始化队列 
void QueueInit(Queue* ps);
// 销毁队列 
void QueueDestroy(Queue* ps);
// 队尾入队列 
void QueuePush(Queue* ps, QDataType data);
// 队头出队列 
void QueuePop(Queue* ps);
// 获取队列头部元素 
QDataType QueueFront(Queue* ps);
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps);
// 获取队列中有效元素个数 
int QueueSize(Queue* ps);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps);

// 初始化队列 
void QueueInit(Queue* ps)
{
	ps->_phead = NULL;
	ps->_ptail = NULL;
	ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{
	assert(ps);
	QNode* cur = ps->_phead;
	while (cur)
	{
		//存下下一个节点的地址,不会出现找空的情况
		QNode* next = cur->_next;
		free(cur);
		cur =next;
	}
}
// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush:newnode:error:");
		exit(1);
	}
	//把需要插入的数值插入到节点里面
	newnode->_next = NULL;
	newnode->_data = x;
	
	// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。
	// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail 
	// 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL 
	// 来检查队列中是否只有一个元素。

	//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较
	//ps->_phead == NULL
	if (ps->size == 0)
	{
		ps->_phead = ps->_ptail = newnode;
	}
	else
	{
		//尾插节点
		ps->_ptail->_next = newnode;
		ps->_ptail= newnode;
	}
	ps->size++;
}
// 队头出队列 
void QueuePop(Queue* ps)
{
	assert(ps && ps->size);
	// 改变头结点
	ps->_phead = ps->_phead->_next;
	ps->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_phead->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_ptail->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{
	return ps->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->size == 0;
}

//匿名结构体(我们需要两个队列,所以采取结构体嵌套的形式解决问题)
typedef struct 
{
    Queue Q1;
    Queue Q2;
} MyStack;

//初始化(关键在于,初始化的数值出去之后就销毁,所以我们需要创建节点进行初始化)
MyStack* myStackCreate() 
{
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    if(obj == NULL)
    {
        perror("obj:error:");
        exit(1);
    }
    QueueInit(&(obj->Q1));
    QueueInit(&(obj->Q2));
    return obj;
}
//入栈 
void myStackPush(MyStack* obj, int x) 
{
    if(QueueEmpty(&(obj->Q1)))//q1为空
    {
        QueuePush(&(obj->Q2), x);
    }
    else
    {
        QueuePush(&(obj->Q1), x);
    }
}

//删除栈顶元素(核心逻辑)
int myStackPop(MyStack* obj) 
{
    //判空(假设法假设q1为空)
    MyStack* Empty = &(obj->Q1);
    MyStack* noEmpty = &(obj->Q2);
    if(QueueEmpty(&(obj->Q2)))//如果q1不为空,q2为空,此时颠倒一下
    {
        Empty = &(obj->Q2);
        noEmpty = &(obj->Q1);
    }
    //循环导入
    while(QueueSize(noEmpty) > 1)//这里的关键点是你不知道谁是空,不是空
    {
        QueuePush(Empty, QueueFront(noEmpty));//这里 是把不空的数值头部元素,循环导入到空的数值里面去
        QueuePop(noEmpty);//并且进行出栈操作
    }
    //结束之后留下了一个,我们保存下来返回这个节点,并且最后进行删除
    int ret = QueueFront(noEmpty);
    QueuePop(noEmpty);
    return ret;
}

//取出栈顶元素
int myStackTop(MyStack* obj) 
{
    if(QueueEmpty(&(obj->Q1)))//q1为空
    {
        return QueueBack(&(obj->Q2));
    }
    else
    {
        return QueueBack(&(obj->Q1));
    }
}
//判空
bool myStackEmpty(MyStack* obj) 
{
    //q1 q2都为空的时候才为空
    return (QueueEmpty(&(obj->Q1)) && QueueEmpty(&(obj->Q2)));
}
//释放空间
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&(obj->Q1));
    QueueDestroy(&(obj->Q2));
    free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

这段代码使用两个队列 Q1Q2 来实现一个栈 MyStack。下面是对每个部分的详细解释:

  1. 数据结构定义

    • QDataType:定义了队列中存储的数据类型,这里使用 int
    • QNode:定义了队列中的节点,包含数据 _data 和指向下一个节点的指针 _next
    • Queue:定义了队列结构,包含指向队列头部和尾部的指针 _phead 和 _ptail,以及记录队列大小的 size
  2. 队列操作函数

    • QueueInit:初始化队列,将头尾指针设置为 NULL,大小设置为0。
    • QueueDestroy:释放队列中所有节点的内存。
    • QueuePush:在队列尾部添加一个新节点。
    • QueuePop:移除队列头部的节点,并减少队列大小。
    • QueueFront:返回队列头部的元素。
    • QueueBack:返回队列尾部的元素。
    • QueueSize:返回队列中元素的数量。
    • QueueEmpty:检查队列是否为空。
  3. 栈操作函数

    • myStackCreate:创建一个新的 MyStack 实例,初始化两个队列 Q1 和 Q2
    • myStackPush:如果 Q1 为空,则将新元素推入 Q2;否则推入 Q1
    • myStackPop:如果 Q2 不为空且 Q1 为空,则交换两个队列的角色。然后,将 Q2 中的元素逐个移动到 Q1,除了最后一个元素。最后,返回并移除 Q2 中的最后一个元素。
    • myStackTop:返回栈顶元素。如果 Q1 不为空,返回 Q1 的尾部元素;否则返回 Q2 的尾部元素。
    • myStackEmpty:如果两个队列都为空,则返回 true,表示栈为空。
    • myStackFree:销毁 MyStack 实例,释放所有相关联的内存。
  4. 使用队列实现栈的逻辑

    • 通过两个队列 Q1 和 Q2 的交替使用,实现了栈的后进先出(LIFO)特性。当一个队列为空时,可以将另一个队列中的元素移动过来,最后一个元素就是栈顶元素。
    • myStackPush 函数根据 Q1 是否为空来决定将新元素推入哪个队列,这样做的目的是保持两个队列中的元素数量大致相等,从而在 myStackPop 操作中能够高效地将元素从一个队列移动到另一个队列。
  5. 注意事项

    • 在 myStackPop 和 myStackTop 函数中,需要检查两个队列的状态,以确定当前栈顶元素所在的队列。
    • myStackDestroy 函数中,确保先销毁队列,再释放 MyStack 实例的内存。

这种使用两个队列实现栈的方法利用了队列的先进先出特性来模拟栈的后进先出特性。这种方法的好处是可以在 O(1) 时间内完成栈的基本操作,同时避免了使用数组实现栈时可能需要的数组扩展和收缩操作。

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值