【数据结构和算法】---栈和队列的互相实现

这篇具有很好参考价值的文章主要介绍了【数据结构和算法】---栈和队列的互相实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、用栈实现队列

具体题目可以参考LeetCode232. 用栈实现队列
首先要想到的是,队列是一种先进先出的结构,而栈是一种先进后出的结构。依此我们可以定义两个栈结构来模拟先进先出,既然要定义两个栈,那么为了方便调用,我们可以将这两个栈结构定义在一个结构体中,如下:

typedef struct {
    ST st1;//栈1
    ST st2;//栈2
} MyQueue;

实现 MyQueue类:

  • void push(int x)将元素 x推到队列的末尾;
  • int pop()从队列的开头移除并返回元素;
  • int peek()返回队列开头的元素;
  • boolean empty()如果队列为空,返回 true;否则,返回 false

1.1初始化队列

我们定义的结构体在主函数外部,为了让每个函数都能用到,那么我们就必须要用malloc来动态开辟空间,这样此结构会被保存在静态区,每次函数调用时便不会销毁此变量,然后再将此结构体中的栈初始化

MyQueue* myQueueCreate() 
{
    MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));//动态开辟结构体变量
    //注意初始化栈的参数为地址
    StackInit(&queue->st1);//初始化栈1
    StackInit(&queue->st2);//初始化栈2
    return queue;
}

1.2模拟入队列

我们可以将栈1作为存数据的栈,那么每次入队列操作就是进栈操作(StackPush(&obj->st1, x);)。

void myQueuePush(MyQueue* obj, int x) 
{
    assert(obj);
    StackPush(&obj->st1, x);
}

1.3模拟出队列

  1. 思路1:
    如果我们用栈1obj->st1来存放数据,在模拟出队列时我们首先要断言栈1不为空,那么当栈1不为空且我们需要出队列头元素时。此时就需要栈2obj->st2来暂存数据,即我们将栈1除栈底的全部元素都出栈并入栈到栈2obj->st2,然后再出栈1最后的元素并返回,这样就模拟了先入先出性质。还需要注意的是在返回最后一个元素前还需要再将所有数据从栈2再入到栈1。逻辑如下:

【数据结构和算法】---栈和队列的互相实现,数据结构和算法,数据结构,算法

  1. 思路2:
    栈1用来存数据,栈2用来出数据。 那么为什么栈2的元素可以直接出呢?当我们需要模拟出队列时,我们可以先将栈1中所以元素出栈并入栈到栈2,这样一来栈2中的top就相当于队列头元素。每次从栈2中出元素时要先判断栈2中是否有元素,若没有,就将栈1中的元素出栈并入栈到栈2中。大致逻辑如下:

【数据结构和算法】---栈和队列的互相实现,数据结构和算法,数据结构,算法

与思路一相比较,思路二栈2无需重新入栈1,还可继续模拟出队列。只能说两种思路各有好处,下列代码实现使用的是思路一:

int myQueuePop(MyQueue* obj) 
{
    assert(obj);
    assert(StackSize(&obj->st1) != 0);//栈1不为空
    ST* empty = &obj->st2;//栈2为空
    ST* noempty = &obj->st1;//栈1不为空
    //将栈1除栈底的所有元素出栈并入栈到栈2
    while(StackSize(noempty) > 1)
    {
        StackPush(empty,StackTop(noempty));
        StackPop(noempty);
    }
    //找到队头
    int ret = StackTop(noempty);
    StackPop(noempty);
    //重新入栈1
    while(StackSize(empty) > 0)
    {
        StackPush(noempty,StackTop(empty));
        StackPop(empty);
    }
    return ret;
}

1.4取模拟的队列头元素

此函数实现与1.3模拟出队列方法相似,就不多介绍了,如下:

int myQueuePeek(MyQueue* obj)
{
    assert(obj);
    ST* empty = &obj->st2;
    ST* noempty = &obj->st1;
    //将栈1除栈底的所有元素出栈并入栈到栈2
    while(StackSize(noempty) > 1)
    {
        StackPush(empty,StackTop(noempty));
        StackPop(noempty);
    }
    //找到队头
    int ret = StackTop(noempty);
    StackPush(empty,ret);
    StackPop(noempty);
    //重新入栈1
    while(StackSize(empty) > 0)
    {
        StackPush(noempty,StackTop(empty));
        StackPop(empty);
    }
    return ret;
}

1.5判断队列是否为空

依据上面思路,因为栈1是用来存数据的,所以当栈1为空时就代表我们模拟的队列为空。

bool myQueueEmpty(MyQueue* obj) 
{
    assert(obj);
    return StackEmpty(&obj->st1);
}

二、用队列实现栈

具体题目可以参考LeetCode225. 用队列实现栈
与用栈实现队列相似,我们同样需要两个队列来模拟实现栈,且关键在于还原队列先入先出的性质,依此性质来实现函数。既然这样我们可以如下定义结构体:

typedef struct 
{
    Queue* q1;//队列1
    Queue* q2;//队列2
} MyStack;

我们可以看到与模拟队列不同的是,模拟栈的结构体中存放的是两个结构体指针,这与队列的实现方法有关。因为我们用的队列是用链表实现的,所以其每个节点都是组成队列的一部分,且我们应该通过指针将他们一个个都连接起来(即通过指针来寻找节点)。
至于用栈实现队列问题中的结构体我们存放的是两个关于栈的结构体,是因为我们所使用的栈使用数组来实现的,这样一来我们操作的就是栈结构体中某一个元素(即动态开辟的数组)。当然在我们也可以放两个栈结构体指针,只不过在下面初始化队列时(myQueueCreate() )我们需要额外malloc动态开辟栈结构大小的空间,然后将指针指向该空间的地址。
实现 MyStack类:

  • void push(int x)将元素 x压入栈顶;
  • int pop()移除并返回栈顶元素;
  • int top()返回栈顶元素;
  • boolean empty()如果栈是空的,返回 true;否则,返回 false

2.1初始化栈

malloc()动态开辟栈结构体没什么问题,与模拟队列相似。但为什么还要给结构体中的两个队列结构体指针动态开辟空间呢?这样不就违背了我们上面探讨的问题了吗?其实不然,这里的两个结构体指针事实上指向的是存放队列头指针和尾指针的结构体,如下:

typedef struct Queue
{
	QNode* phead;//队列头指针
	QNode* ptail;//队列尾指针
	int size;//长度
}Queue;

这样一来,基本每个函数都需要用到此结构体,那么我们就必须malloc开辟来增加作用域和生命周期。 最后再初始化这两个存放头/尾指针的结构体,并返回用来模拟栈的结构体地址。

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;
}

2.2模拟出栈

与模拟出队列不同的是,这里用来模拟出栈的两个队列都可以用来出栈和入栈,具体方法如下:
为了还原栈先入后出的性质,我们可以先找到不为空的队列(因为两个队列都有可能有数据,但不同时有),然后将有数据的队列(noempty)除队尾的一个节点全都出队列并入队列到无数据的队列(empty,这样一来就找到了尾节点(模拟的栈顶)。还需要注意的是,此时我们无需再将数据重新入到noempty 逻辑大致如下:

【数据结构和算法】---栈和队列的互相实现,数据结构和算法,数据结构,算法

int myStackPop(MyStack* obj) 
{
    //先假设队列1为空
    Queue* empty = obj->q1;
    Queue* noempty = obj->q2;
    //纠正
    if(QueueEmpty(obj->q2))
    {
        empty = obj->q2;
        noempty = obj->q1;
    }
    //noempty出,并入到empty
    while(QueueSize(noempty) > 1)
    {
        int cmp = QueueFront(noempty);
        QueuePop(noempty);
        QueuePush(empty, cmp);
    }
    //取到模拟的栈顶元素
	int ret = QueueFront(noempty);
	QueuePop(noempty);
    return ret;
}

2.3模拟入栈

依据上面的方法,我们是要将数据入到不为空的队列,简单的if语句便可完成筛选。

void myStackPush(MyStack* obj, int x) 
{
    assert(obj);
    if(!QueueEmpty(obj->q1))
    {
        QueuePush(obj->q1, x);
    }
    else
    {
        QueuePush(obj->q2, x);
    }
}

2.4取模拟的栈顶元素

同样我们需要找到不为空的那个队列,且事实上队列尾指针指向的那个节点就是模拟的栈的栈顶,我们只需返回此元素即可。

int myStackTop(MyStack* obj) 
{
    assert(obj);
    //找不为空的队列
    if(!QueueEmpty(obj->q1))
        return QueueBack(obj->q1);
    else
        return QueueBack(obj->q2);
}

2.5判读栈是否为空

当两个队列都没有数据时,那么模拟的栈就是空栈。文章来源地址https://www.toymoban.com/news/detail-768470.html

bool myStackEmpty(MyStack* obj) 
{
    assert(obj);
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

到了这里,关于【数据结构和算法】---栈和队列的互相实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【数据结构】实现栈和队列

    【数据结构】实现栈和队列

    (1)栈的概念 栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底 。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈, 入数据在栈顶。 出

    2024年02月11日
    浏览(10)
  • 【数据结构】栈和队列的模拟实现

    【数据结构】栈和队列的模拟实现

    前言:前面我们学习了单链表并且模拟了它的实现,今天我们来进一步学习,来学习栈和队列吧!一起加油各位,后面的路只会越来越难走需要我们一步一个脚印! 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:数据结构 👈 💯代码仓库:卫卫周大胖的学习日记💫 💪关

    2024年02月05日
    浏览(13)
  • 数据结构(Java实现)-栈和队列

    数据结构(Java实现)-栈和队列

    栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 先进后出 栈的使用 栈的模拟实现 上述的主要代码 改变元素的序列 将递归转化为循环 比如:逆序打印链表 结果如下 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表

    2024年02月10日
    浏览(10)
  • c++实现数据结构栈和队列

    c++实现数据结构栈和队列

    1、栈 头文件 源文件 主函数 2、循环队列 头文件 源文件 主函数 3、思维导图

    2024年02月08日
    浏览(9)
  • 数据结构——Java实现栈和队列

    数据结构——Java实现栈和队列

    (1)栈是一种线性数据结构 (2)规定只能从栈顶添加元素,从栈顶取出元素 (3)是一种先进后出的数据结构(Last First Out)LIFO Java中可以直接调用方法来实现栈 如何自己写代码来实现栈呢? 先定义一个接口,方便后边进行调用 接下来来实现栈的方法,调用接口,完善方法

    2024年01月20日
    浏览(10)
  • 数据结构基础5:栈和队列的实现。

    数据结构基础5:栈和队列的实现。

    1.基本概念 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈

    2024年02月13日
    浏览(8)
  • 【数据结构】栈和队列的模拟实现(两个方式实现)

    💓作者简介: 加油,旭杏,目前大二,正在学习 C++ , 数据结构 等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏👀 🚚代码仓库:旭日东升 1👀 🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖        这一篇博客将学习栈和队列的相关知识, 栈

    2024年02月05日
    浏览(9)
  • 数据结构初阶(用C语言实现简单数据结构)--栈和队列

    数据结构初阶(用C语言实现简单数据结构)--栈和队列

    ✨✨欢迎来到T_X_Parallel的博客!!       🛰️博客主页:T_X_Parallel       🛰️专栏 : 数据结构初阶       🛰️欢迎关注:👍点赞🙌收藏✍️留言 这小猫真好看 言归正传,通过上篇有关顺序表和链表的博客,可以了解到线性表的一些大致特征,这篇博

    2024年02月08日
    浏览(10)
  • 数据结构上机实验——栈和队列的实现、栈和队列的应用、进制转换、约瑟夫环问题

    数据结构上机实验——栈和队列的实现、栈和队列的应用、进制转换、约瑟夫环问题

      1.利用栈的基本操作实现将任意一个十进制整数转化为R进制整数。   2.利用循环队列实现.约瑟夫环问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到k的那个人出圈;他的下一个人又从1开始报数,数到k的那个人出圈;依

    2024年02月08日
    浏览(10)
  • 数据结构入门(C语言版)栈和队列之队列的介绍及实现

    数据结构入门(C语言版)栈和队列之队列的介绍及实现

    什么是队列呢?我们先看下面的图: 我们可以理解成高速公路上的隧道,根据这个图的描述 我们把需入队的元素看作一辆车,把队列看作隧道,由此我们可以看出 队列的特点是 只允许从一端进入,从另一端离开。 队列就是只允许在一端进行插入数据操作,在另一端进行删

    2023年04月15日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包