【栈与队列】栈与队列的相互转换OJ题

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


1 栈与队列

1.1 栈

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

1.2 队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的原则

入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

1.3 差别与关系

通过上述的介绍,栈与队列仿佛毫不相干,一个先入先出,一个后入后出。

栈像一个容器来装物品,队列像排队买饭。这两个事情看起来毫不相干,那么如何实现栈与队列的相互转换呢。下面我们来看两道OJ题,来进行具体解决。

2 栈与队列的相互转换

2.1 队列模拟实现栈

我们来看题目描述

这道题给了我们六个接口,接下来我们来逐一完成。

首先先把队列的代码拷贝到代码区,方便我们使用队列中的对应接口。、

2.1.1 栈的结构体设置

首先,我们来分析一下怎样通过队列来模拟栈

我们看,我们模拟一下发现,删除操作只需要将一个队列的前n个数据迁移到另一个队列就可以,那我们不妨就假设看看两个队列能否实现栈。

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

2.1.2 初始化接口

非常简单的我们初始化一下

MyStack* myStackCreate() {
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(st->q1);
    QueueInit(st->q2);
    return st

2.1.3 压栈操作

void myStackPush(MyStack* obj, int x) {
    Queue* emptyQueue = &obj->q1;
    Queue* unemptyQueue = &obj->q2;
    if (!QueueEmpty(emptyQueue)) {
        emptyQueue = &obj->q2;
        unemptyQueue = &obj->q1;
    }
    QueuePush(unemptyQueue, x);
}

使用假设法来判断两个队列的空与非空,选择非空进行插入。

2.1.4 出栈

int myStackPop(MyStack* obj) {

    Queue* emptyQueue = &obj->q1;
    Queue* unemptyQueue = &obj->q2;
    if (!QueueEmpty(emptyQueue)) {
        emptyQueue = &obj->q2;
        unemptyQueue = &obj->q1;
    }

    while (QueueSize(unemptyQueue) > 1) {
        QueuePush(emptyQueue, unemptyQueue->front->data);
        QueuePop(unemptyQueue);
    }
    int ret = QueueFront(unemptyQueue);
    QueuePop(unemptyQueue);
    return ret;
}

依然是通过假设法来判断是否非空,然后依次将非空队列的前n-1个元素移动到空队列,这里的移动不是将节点的移动,而是将值赋给空队列。最后取栈顶 返回值,再出栈一下非空队列,完成操作。

2.1.5 取栈顶

int myStackTop(MyStack* obj) {

    if (!QueueEmpty(&obj->q1)) {
        return QueueBack(&obj->q1);
    }
    else {
        return QueueBack(&obj->q2);
    }

}

选择判断是否为非空进行取队列顶操作即可。

2.1.6 判断是否为空

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

都为空才为空

2.1.7 销毁栈

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
    obj = NULL;
}

一步一步free掉即可。

2.2 栈模拟实现队列

我将源码放在下面,原理与上述相似,请自行观看。

2.2.1 版本一

typedef struct {
    Stack st1;
    Stack st2; 
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&pq->st1);
    StackInit(&pq->st2);

    return pq;
}

void myQueuePush(MyQueue* obj, int x) {
    if(!StackEmpty(&obj->st1)){
        StackPush(&obj->st1,x);
    }
    else{
        StackPush(&obj->st2,x);
    }

}

int myQueuePop(MyQueue* obj) {
    //判断 空与非空
    Stack* empty_st = &obj->st1;
    Stack* nonempty_st = &obj->st2;
    if(!StackEmpty(&obj->st1)){
        empty_st = &obj->st2;
        nonempty_st = &obj->st1;
    }
    //转移元素
    while(StackSize(nonempty_st)>1){
        StackPush(empty_st,StackTop(nonempty_st));
        StackPop(nonempty_st);
    }
    
    int del = StackTop(nonempty_st);
    StackPop(nonempty_st);
    while(!StackEmpty(empty_st)){
        StackPush(nonempty_st,StackTop(empty_st));
        StackPop(empty_st);
    }
    return del;
    
}

int myQueuePeek(MyQueue* obj) {
     //判断 空与非空
    Stack* empty_st = &obj->st1;
    Stack* nonempty_st = &obj->st2;
    if(!StackEmpty(&obj->st1)){
        empty_st = &obj->st2;
        nonempty_st = &obj->st1;
    }
    while(StackSize(nonempty_st)>1){
        StackPush(empty_st,StackTop(nonempty_st));
        StackPop(nonempty_st);
    }
    int ret = StackTop(nonempty_st);
    while(!StackEmpty(empty_st)){
        StackPush(nonempty_st,StackTop(empty_st));
        StackPop(empty_st);
    }
    return ret;
}

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

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->st1);
    StackDestroy(&obj->st2);
    free(obj);
}

2.2.2 优化版本二

Thanks♪(・ω・)ノ


目录
打赏
0
0
0
0
43
分享
相关文章
|
2月前
|
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
32 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
3月前
|
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
183 77
|
3月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
73 7
|
3月前
|
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
103 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
3月前
|
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
66 9
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
122 5
|
5月前
|
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
89 0
|
5月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
136 58
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
156 21