数据结构--》深入了解栈和队列,让算法更加高效

news2024/12/25 8:53:35

        本文将带你深入了解数据结构栈和队列,这两种基础的线性数据结构在算法中的重要性不言而喻。我们将会详细介绍栈和队列的概念、分类、实现以及应用场景,在理解栈和队列的基础上,还将探讨如何通过栈和队列来高效地解决算法问题。

        无论你是初学者还是进阶者,本文将为你提供简单易懂、实用可行的知识点,帮助你更好地掌握栈和队列,进而提升算法解题的能力,开启数据结构与算法的奇妙之旅。

目录

栈的基本概念

栈的顺序存储与链式存储实现

栈在表达式中的应用

队列的基本概念

队列的顺序和链式实现


栈的基本概念

        栈(Stack)是一种线性数据结构,它的特点是只能在末端(栈顶)进行插入和删除操作,并且访问元素时也是从栈顶开始。栈的行为类似于弹簧被压缩的过程,当一个元素被插入栈中时,它就被压入栈底;而当一个元素被删除时,它就从栈顶弹出。

        由于栈的后进先出(Last-In-First-Out,LIFO)的特性,因此它常用于需要“回溯”的算法实现,例如递归、深度优先搜索等。在计算机科学中,栈也被广泛应用于表达式求值、函数调用、内存管理等方面。

栈可以通过数组或链表来实现。通过数组实现的栈称为顺序栈,而通过链表实现的栈称为链式栈。无论通过何种方式实现,栈的基本操作包括入栈(push)、出栈(pop)、查看栈顶元素(top)以及判断栈是否为空(empty)。

栈的基本操作如下

InitStack(&S):初始化栈。构造一个空栈S,分配内存空间。

DestroyStack(&L):销毁栈。销毁并释放栈S所占用的内存空间。

Push(&S,x):进栈,若栈S未满,则将x加入使之成为栈顶。

Pop(&S,&x):出栈,若栈S非空,则弹出栈顶元素,并用x返回。

GetTop(S,&x):读栈顶元素。若栈S非空,则用x返回栈顶元素

其他常用操作

StackEmpty(S):判断一个栈S是否为空。若S为空,则返回true,否则返回false。

栈的常考题型为:

回顾重点,其主要内容整理成如下内容: 

栈的顺序存储与链式存储实现

栈可以通过两种方式来实现:顺序存储链式存储

        顺序存储的栈,即顺序栈,通常使用数组来实现。在顺序栈中,栈底在数组的低地址处,栈顶在数组的高地址处。栈顶指针(top)是一个整型变量,它指向当前栈顶元素所在的位置。当栈为空时,top的值为-1。入栈操作就是将元素插入到top位置的后面,然后将top指针向上移动一个位置;出栈操作就是将top指针向下移动一个位置,然后将该位置的元素删除。

const int MAX_SIZE = 100; // 定义栈的最大容量
int stack[MAX_SIZE]; // 定义栈数组
int top = -1; // 初始化栈顶指针为-1,表示栈为空

// 入栈操作
void push(int x) {
    if (top == MAX_SIZE - 1) { // 栈满,无法继续插入元素
        cout << "Error: Stack overflow!" << endl;
        return;
    }
    top++; // 栈顶指针上移
    stack[top] = x; // 将元素x插入栈顶位置
}

// 出栈操作
void pop() {
    if (top == -1) { // 栈空,无法进行出栈操作
        cout << "Error: Stack underflow!" << endl;
        return;
    }
    top--; // 栈顶指针下移
}

// 获取栈顶元素
int getTop() {
    if (top == -1) { // 栈为空,栈顶指针无效
        cout << "Error: Stack is empty!" << endl;
        return -1;
    }
    return stack[top]; // 返回栈顶元素
}

// 判断栈是否为空
bool isEmpty() {
    return top == -1; // 栈顶指针为-1时,栈为空
}

栈的顺序存储还有一种共享栈的方式。共享栈是一种特殊的顺序栈,它可以同时存储两个栈。具体来说,共享栈有两个栈顶指针,分别指向两个栈的栈顶元素。它们从两端向中间生长,当它们相遇时就表示栈满了。共享栈常用于两个栈需要共用一段连续的存储空间的情况,例如某些操作系统的内核栈和用户栈都共用一个物理内存区域。下面是使用 C++ 实现共享栈的完整代码示例:

#include <iostream>
using namespace std;

const int MAX_SIZE = 100; // 定义栈的最大容量

int stack[MAX_SIZE]; // 定义存储栈元素的数组
int top1 = -1; // 定义第一个栈的栈顶指针,初始值为-1
int top2 = MAX_SIZE; // 定义第二个栈的栈顶指针,初始值为MAX_SIZE

// 向第一个栈中插入元素
void pushStack1(int x) {
    if (top1 + 1 == top2) { // 共享栈已满
        cout << "Error: Stack overflow!" << endl;
        return;
    }
    top1++; // 第一个栈的栈顶指针上移
    stack[top1] = x; // 在第一个栈中插入元素
}

// 向第二个栈中插入元素
void pushStack2(int x) {
    if (top1 + 1 == top2) { // 共享栈已满
        cout << "Error: Stack overflow!" << endl;
        return;
    }
    top2--; // 第二个栈的栈顶指针下移
    stack[top2] = x; // 在第二个栈中插入元素
}

// 从第一个栈中删除元素
void popStack1() {
    if (top1 == -1) { // 第一个栈为空
        cout << "Error: Stack underflow!" << endl;
        return;
    }
    top1--; // 第一个栈的栈顶指针下移
}

// 从第二个栈中删除元素
void popStack2() {
    if (top2 == MAX_SIZE) { // 第二个栈为空
        cout << "Error: Stack underflow!" << endl;
        return;
    }
    top2++; // 第二个栈的栈顶指针上移
}

// 获取第一个栈的栈顶元素
int getTop1() {
    if (top1 == -1) { // 第一个栈为空,栈顶指针无效
        cout << "Error: Stack is empty!" << endl;
        return -1;
    }
    return stack[top1]; // 返回第一个栈的栈顶元素
}

// 获取第二个栈的栈顶元素
int getTop2() {
    if (top2 == MAX_SIZE) { // 第二个栈为空,栈顶指针无效
        cout << "Error: Stack is empty!" << endl;
        return -1;
    }
    return stack[top2]; // 返回第二个栈的栈顶元素
}

// 判断第一个栈是否为空
bool isEmpty1() {
    return top1 == -1; // 第一个栈的栈顶指针为-1时,栈为空
}

// 判断第二个栈是否为空
bool isEmpty2() {
    return top2 == MAX_SIZE; // 第二个栈的栈顶指针为MAX_SIZE时,栈为空
}

int main() {
    pushStack1(1);
    pushStack1(2);
    pushStack2(3);
    pushStack2(4);

    cout << "Top of Stack 1: " << getTop1() << endl;
    cout << "Top of Stack 2: " << getTop2() << endl;

    popStack1();
    popStack2();

    cout << "Top of Stack 1: " << getTop1() << endl;
    cout << "Top of Stack 2: " << getTop2() << endl;

    return 0;
}

在主函数中,我们演示了如何使用 pushStack1()、pushStack2()、popStack1()、popStack2()、getTop1() 和 getTop2() 等操作来对共享栈进行插入、删除和获取元素等操作,并输出了两个栈的栈顶元素。

回顾重点,其主要内容整理成如下内容:  

栈的链式存储实现方式是使用链表来存储栈中的元素。栈顶指针 Top 指向链表的头节点,每插入一个元素就在链表的头部插入一个节点,每删除一个元素就删除链表头部的节点,这样就实现了栈的先进后出的特性。 以下是使用 C++ 语言实现的栈链式存储的完整代码:

#include <iostream>
using namespace std;

// 定义链表的节点结构体
struct Node {
    int data; // 节点中存储的数据
    Node* next; // 指向下一个节点的指针
};

// 定义链表的头指针和栈顶指针
Node* head = NULL;
Node* top = NULL;

// 向栈中插入元素
void push(int x) {
    Node* newNode = new Node; // 创建新节点
    newNode->data = x; // 将元素值赋给新节点的 data 域
    newNode->next = top; // 新节点的 next 指向当前栈顶节点
    top = newNode; // 更新栈顶指针
}

// 从栈中删除元素
void pop() {
    if (top == NULL) { // 栈为空,无法删除
        cout << "Error: Stack is empty!" << endl;
        return;
    }
    Node* temp = top; // 暂存当前栈顶节点的地址
    top = top->next; // 更新栈顶指针
    delete temp; // 释放暂存节点内存空间
}

// 获取栈顶元素
int getTop() {
    if (top == NULL) { // 栈为空,栈顶指针无效
        cout << "Error: Stack is empty!" << endl;
        return -1;
    }
    return top->data; // 返回栈顶节点的 data 域
}

// 判断栈是否为空
bool isEmpty() {
    return top == NULL; // 栈顶指针为空时,栈为空
}

int main() {
    push(1);
    push(2);
    push(3);

    cout << "Top of Stack: " << getTop() << endl;

    pop();
    pop();

    cout << "Top of Stack: " << getTop() << endl;

    return 0;
}

在主函数中,我们演示了如何使用 push()、pop() 和 getTop() 等操作来对栈进行插入、删除和获取元素等操作,并输出了栈顶元素。 

栈在表达式中的应用

栈在表达式中的应用非常广泛,可以用来进行中缀表达式的转换计算后缀表达式等操作。中缀表达式是指形如 a+b 或 (a+b)*c-d/(e+f) 这样的表达式,其中运算符位于两个操作数的中间。但是在计算机中,通常采用后缀表达式(也叫逆波兰表达式)来表示算术表达式,即把运算符放在操作数后面,例如 a b + c * d /。

队列的基本概念

        队列是一种线性数据结构,具有先进先出(FIFO)的特点。与栈不同,队列只允许在队尾插入元素,在队头删除元素。队列通常用于需要按照时间顺序处理事物的场合,例如多任务并发处理或者打印机缓存任务等,它们按照顺序依次加入队列,并按照先进先出的顺序进行处理。

队列的基本操作如下:

InitQueue(&Q):初始化队列,构造一个空队列Q。

DestroyQueue(&Q):销毁队列。销毁并释放队列Q所占用的内存空间。

EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。

DeQueue(&Q,&x):出队,若队列Q非空,删除对头元素,并用x返回。

GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x。

回顾重点,其主要内容整理成如下内容:  

队列的顺序和链式实现

队列可以用数组或链表等数据结构实现,分别称为顺序队列链式队列

顺序队列是基于数组的实现,使用一个数组来存储队列中的元素,使用头尾指针分别指向队列的头部和尾部。新元素插入到队列尾部,旧元素从队列头部删除,每次插入或删除元素后需要更新头尾指针。缺点是当队列元素个数达到数组容量时,需要进行数据搬移,时间复杂度为 O(n)。以下是一个 C++ 语言实现的顺序队列的简单示例代码:

#include <iostream>
using namespace std;

const int MAXSIZE = 100; // 队列的最大容量
int queue[MAXSIZE]; // 用数组实现队列
int head = 0, tail = 0; // 头尾指针

// 向队列尾部插入元素
void enqueue(int x) {
    if (tail == MAXSIZE) { // 队列已满
        cout << "Error: Queue is full!" << endl;
        return;
    }
    queue[tail++] = x; // 将新元素添加到队列尾部,同时更新尾指针
}

// 从队列头部删除元素
void dequeue() {
    if (head == tail) { // 队列为空
        cout << "Error: Queue is empty!" << endl;
        return;
    }
    head++; // 更新头指针,删除队首元素
}

// 获取队头元素
int getFront() {
    if (head == tail) { // 队列为空
        cout << "Error: Queue is empty!" << endl;
        return -1;
    }
    return queue[head]; // 返回队首元素
}

// 判断队列是否为空
bool isEmpty() {
    return head == tail; // 头尾指针相等时,队列为空
}

int main() {
    enqueue(1);
    enqueue(2);
    enqueue(3);

    cout << "Front of Queue: " << getFront() << endl;

    dequeue();
    dequeue();

    cout << "Front of Queue: " << getFront() << endl;

    return 0;
}

需要注意的是,在顺序队列中,每次删除元素时并没有真正将元素从数组中移除,而是单纯地将头指针向后移动。因此,当头指针后面有很多无用元素时,需要调整数组内部的元素位置,以节省内存空间。

回顾重点,其主要内容整理成如下内容:  

链式队列是基于链表的实现,使用一个链表来存储队列中的元素,使用头指针指向队首节点,尾指针指向队尾节点。新元素插入到链表尾部,旧元素从链表头部删除,每次插入或删除元素只需操作指针即可,无需进行数据搬移。链式队列相对于顺序队列的优点是没有容量限制,但由于链表需要额外的指针存储,空间开销较大一些。 以下是一个 C++ 语言实现的顺序队列的简单示例代码:

#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
};

Node* head = NULL; // 头指针指向队列头部
Node* tail = NULL; // 尾指针指向队列尾部

// 向队列尾部插入元素
void enqueue(int x) {
    Node* newNode = new Node;
    newNode->data = x;
    newNode->next = NULL;

    if (tail == NULL) { // 队列为空
        head = tail = newNode;
        return;
    }
    tail->next = newNode; // 将新节点添加到队列尾部
    tail = newNode; // 更新尾指针
}

// 从队列头部删除元素
void dequeue() {
    if (head == NULL) { // 队列为空
        cout << "Error: Queue is empty!" << endl;
        return;
    }
    Node* temp = head;
    head = head->next; // 更新头指针
    if (head == NULL) { // 如果队列中只有一个元素,删除后需要更新尾指针
        tail = NULL;
    }
    delete temp;
}

// 获取队头元素
int getFront() {
    if (head == NULL) { // 队列为空
        cout << "Error: Queue is empty!" << endl;
        return -1;
    }
    return head->data;
}

// 判断队列是否为空
bool isEmpty() {
    return head == NULL; // 头指针为空时,队列为空
}

int main() {
    enqueue(1);
    enqueue(2);
    enqueue(3);

    cout << "Front of Queue: " << getFront() << endl;

    dequeue();
    dequeue();

    cout << "Front of Queue: " << getFront() << endl;

    return 0;
}

需要注意的是,在链式队列中,每次删除元素时需要注意特殊情况,比如队列中只有一个元素的情况。还需要注意内存泄漏问题,每次删除元素后需要释放对应节点的内存空间。 

回顾重点,其主要内容整理成如下内容:  

双端队列的介绍:双端队列(deque)是一种可以在队列两端进行插入和删除操作的数据结构。它可以被看作是两个栈拼接而成,因此常常被称为“双端栈”。

回顾重点,其主要内容整理成如下内容:  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/669983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LTV-6341-ASEMI代理台湾光宝储能专用光耦LTV-6341

编辑&#xff1a;ll LTV-6341-ASEMI代理台湾光宝储能专用光耦LTV-6341 型号&#xff1a;LTV-6341 品牌&#xff1a;台湾光宝 封装&#xff1a;LSOP-6 工作温度&#xff1a;-40C~125C LTV-6341特性&#xff1a; 3.0A峰值输出电流驱动能力 轨对轨输出电压 200 ns最大传播…

D. Survey in Class(贪心 + 分类讨论)

Problem - D - Codeforces Zinaida Viktorovna 的历史课上有 n 名学生。今天的作业包括 m 个主题&#xff0c;但是学生们准备时间很短&#xff0c;所以第 i 个学生只学习了从 li 到 ri &#xff08;包括&#xff09;的主题。 在课开始时&#xff0c;每个学生都将手举在 0 处。…

虚幻引擎程序化资源生成框架PCG(Procedural Content Generation Framework) 之一 PCG基础

可以和Houdini说拜拜了 文章目录 0. 概述1. 启动插件2. 工作逻辑2.1 添加PCGVolume2.2 创建PCGGraph2.3 编写PCGGraph逻辑 小结 0. 概述 Unreal Engine 5.2全新推出了程序化资源生成框架即Procedural Content Generation Framework下文简称PCG&#xff0c; 开发者可以通过PCG程…

CSDN 每日一练及周赛介绍

CSDN 每日一练及周赛介绍 每日一练每日一练入口 CSDN 周赛CSDN 周赛入口 相关社区每日一练社区入口CSDN 竞赛专区社区入口 每日一练题库每日一练速查每日一练题目题解速查入口 每日一练题解自行接管输入 根据题号进入题目预习提交新题目CSDN 编程比赛出题规范 吐槽 每日一练 C…

JavaWeb项目乱码问题

设置编码格式有三个地方&#xff0c; 一,用于设置网页发出到服务器上的编码格式为UTF-8&#xff0c;一般该代码会自动创建。 <% page contentType"text/html;charsetUTF-8" language"java" %> 二&#xff0c;服务器响应后发送的文件的编码格式为U…

从BNO055传感器获取IMU数据-1

最近学习惯导相关知识&#xff0c;BNO055是博世的绝对方向传感器&#xff0c;它结合了传感器数据和微处理器来过滤和组合数据&#xff0c;为用户提供空间中的绝对方向。 关于BNO055传感器 BNO055使用三个三轴传感器同时测量切向加速度&#xff08;通过加速度计&#xff09;&a…

【认知提升思维篇】之心灵之力的自我认可模式

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;普本…

深入浅出vite(一)--vite的优点及原理、性能优化

Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#xff0c;有些模板需要依赖更高的 Node 版本才能正常运行&#xff0c;当你的包管理器发出警告时&#xff0c;请注意升级你的 Node 版本。 背景 webpack支持多种模块化&#xff0c;将不同模块的依赖关系构建成依赖图来进行…

DevExpress WinForms功能区组件,让业务应用创建更轻松!(下)

DevExpress WinForms的Ribbon&#xff08;功能区&#xff09;组件灵感来自于Microsoft Office&#xff0c;并针对WinForms开发人员进行了优化&#xff0c;它可以帮助开发者轻松地模拟当今最流行的商业生产应用程序。 在上文中&#xff08;点击这里回顾>>&#xff09;&am…

Axure教程—中继器菜单

本文将教大家如何用AXURE中的中继器制作菜单&#xff08;不自动折叠其他菜单&#xff09; 一、效果 预览地址&#xff1a;https://8ao8gl.axshare.com 二、功能 1、点击菜单出现相应的子菜单 2、子菜单如果想折叠&#xff0c;点击相应的菜单 三、制作 &#xff08;1&#xff…

Java-@Transactional注解超详细

本文已收录于专栏 《Java》 目录 本文前言概念说明使用说明底层实现注意事项注解扩展总结提升 本文前言 Transactional注解是Spring框架中用于声明式事务管理的关键注解。本文将深入探讨Transactional注解的作用、使用方式和常见属性&#xff0c;并结合代码实例演示其在实际项目…

Adobe PS 2023、Adobe Photoshop 2023下载教程、安装教程

最后附下载地址 Adobe Photoshop 简介&#xff1a; Adobe Photoshop是一款广泛使用的图像处理软件&#xff0c;由Adobe公司开发。它提供了许多强大的工具和功能&#xff0c;可以用于图像编辑、合成、修饰、设计等各个领域。用户可以使用Photoshop来调整图像的亮度、对比度、色…

《水经注地图服务》如何快速发布墨卡托DAT缓存

《水经注地图服务》的快速发布功能是一个能够帮助用户实现快速发布地图服务的功能&#xff0c;并且提供常规情况下大多数用户所需的默认配置&#xff0c;让用户在发布地图时更加便捷。 前面为大家分享了《水经注地图服务》快速发布经纬度DAT缓存以及如何在水经微图中加载&…

项目管理甘特图,怎么做才能更高效?(附甘特图详细制作教程和模板)

如何制作项目管理的甘特图&#xff1f;给大家放几个模板感受下&#xff1a; 01 项目管理Excel套表 02 工程项目流程甘特图 03 项目进度横道图 04 生产制造排程规划图 05 项目日程表 06 项目进度计划表 甘特图制作教程&#xff0c;一共两种方法&#xff0c;大家按需选择&#x…

android注入so或者dex

本程序分为32位和64位&#xff0c;以及so中加载apk&#xff08;或者dex都可以&#xff09;。 代码地址&#xff1a;点击下载 &#xff08;一&#xff09;so注入 32位和64位so注入代码几乎相同&#xff0c;因此仅以32位为例说明so注入的过程。 arm64-v8a架构可以兼容armeabi…

SLF4J门面日志框架源码探索 | 京东云技术团队

1 SLF4J介绍 SLF4J即Simple Logging Facade for Java&#xff0c;它提供了Java中所有日志框架的简单外观或抽象。因此&#xff0c;它使用户能够使用单个依赖项处理任何日志框架&#xff0c;例如&#xff1a;Log4j&#xff0c;Logback和JUL(java.util.logging)。通过在类路径中…

单片机Hard fault 产生原因和错误跟踪的方法

一、单片机 Hard fault产生的原因 Hard fault产生的原因有两方面&#xff0c;硬件方面和软件方面。 ①硬件方面常见原因&#xff1a; 电源设计有错误&#xff0c;造成器件供电不稳&#xff1b; 电源质量不好&#xff0c;纹波&#xff0c;噪声过大&#xff1b; 器件接地不良&…

干货分享|HOOPS Web平台和Polygonica进行增材制造的云CAM服务示例

这篇文章提供了一个示例项目&#xff0c;展示了使用 Machineworks Polygonica 和 HOOPS Web 平台进行增材制造的云 CAM 服务。该项目作为一个示例&#xff0c;说明了如何在服务器端使用 Polygonica 与 HOOPS Communicator 和 Exchange 来开发云服务。 它涵盖了增材制造 CAM 的…

Android问题笔记-Android Studio编译报错:2 files found with path.....

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…

看物联网技术ZETA如何帮助场馆实现数智化管理升级?

背景介绍&#xff1a; 江宁足球训练基地位于南京江宁区上坊镇境内,是江苏省女足及青少年足球发展基地。该基地总占地面积为333300平方米&#xff0c;其中房屋建筑面积有19000平方米&#xff0c;健身房350平方米&#xff0c;拥有9个标准足球场&#xff0c;曾承办多场甲级足球赛…