【Hello算法】 > 第 3 关 >栈与队列

数据结构 之 数组与链表

    • 1 栈 / 栈的常见操作、实现、应用
    • 2 队列 /队列的常见操作、实现、应用
    • 3 双向队列
    • 4 Tips

———————————————————————————————————————————————————————————-
————————————————————Hello算法—速通笔记—第三集—start———————–———————————————-

1 栈 / 栈的常见操作、实现、应用

栈(stack)是一种遵循 先入后出 逻辑的线性数据结构。
堆叠元素的顶部称为 “栈顶”,底部称为“栈底”。将把元素添加到栈顶的操作叫作“入栈”,删除栈顶元素的操作叫作“出栈”
在这里插入图片描述

栈的常用操作
在这里插入图片描述

/* 初始化栈 */
stack<int> stack;
/* 元素入栈 */
stack.push(1);
stack.push(3);
stack.push(2);
stack.push(5);
stack.push(4);/* 访问栈顶元素 */
int top = stack.top();
/* 元素出栈 */
stack.pop(); // 无返回值
/* 获取栈的长度 */
int size = stack.size();
/* 判断是否为空 */
bool empty = stack.empty();

栈的实现:
栈可以视为一种受限制的数组或链表。

/* 基于链表实现的栈 */
class LinkedListStack {private:ListNode *stackTop; // 将头节点作为栈顶int stkSize;        // 栈的长度public:LinkedListStack() {stackTop = nullptr;stkSize = 0;}~LinkedListStack() {// 遍历链表删除节点,释放内存freeMemoryLinkedList(stackTop);}/* 获取栈的长度 */int size() {     return stkSize;    }/* 判断栈是否为空 */bool isEmpty() {      return size() == 0;    }/* 入栈 */void push(int num) {ListNode *node = new ListNode(num);node->next = stackTop;stackTop = node;stkSize++;}/* 出栈 */int pop() {int num = top();ListNode *tmp = stackTop;stackTop = stackTop->next;// 释放内存delete tmp;stkSize--;return num;}/* 访问栈顶元素 */int top() {if (isEmpty())throw out_of_range("栈为空");return stackTop->val;}/* 将 List 转化为 Array 并返回 */vector<int> toVector() {ListNode *node = stackTop;vector<int> res(size());for (int i = res.size() - 1; i >= 0; i--) {res[i] = node->val;node = node->next;}return res;}
};
/* 基于数组实现的栈 */
class ArrayStack {private:vector<int> stack;public:/* 获取栈的长度 */int size() {     return stack.size();    }/* 判断栈是否为空 */bool isEmpty() {      return stack.size() == 0;    }/* 入栈 */void push(int num) {      stack.push_back(num);    }/* 出栈 */int pop() {int num = top();stack.pop_back();return num;}/* 访问栈顶元素 */int top() {if (isEmpty())throw out_of_range("栈为空");return stack.back();}/* 返回 Vector */vector<int> toVector() {     return stack;    }
};

对比两种实现:
时间效率:
基于数组实现的栈在触发扩容时效率会降低,但由于扩容是低频操作,因此平均效率更高。
基于链表实现的栈可以提供更加稳定的效率表现。
空间效率:
基于数组实现的栈可能造成一定的空间浪费。
由于链表节点需要额外存储指针,因此链表节点占用的空间相对较大。需要针对具体情况进行分析。
栈的应用:

  • 浏览器中的后退与前进、软件中的撤销与反撤销。
  • 程序内存管理。

2 队列 /队列的常见操作、实现、应用

队列(queue)是一种遵循 先入先出 规则的线性数据结构。
在这里插入图片描述队列的常见操作与实现

/* 初始化队列 */
queue<int> queue;
/* 元素入队 */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);/* 访问队首元素 */
int front = queue.front();
/* 元素出队 */
queue.pop();
/* 获取队列的长度 */
int size = queue.size();
/* 判断队列是否为空 */
bool empty = queue.empty();
/* 基于链表实现的队列 */
class LinkedListQueue {private:ListNode *front, *rear; // 头节点 front ,尾节点 rearint queSize;public:LinkedListQueue() {front = nullptr;rear = nullptr;queSize = 0;}~LinkedListQueue() {// 遍历链表删除节点,释放内存freeMemoryLinkedList(front);}/* 获取队列的长度 */int size() {     return queSize;    }/* 判断队列是否为空 */bool isEmpty() {      return queSize == 0;    }/* 入队 */void push(int num) {// 在尾节点后添加 numListNode *node = new ListNode(num);// 如果队列为空,则令头、尾节点都指向该节点if (front == nullptr) {front = node;rear = node;}// 如果队列不为空,则将该节点添加到尾节点后else {rear->next = node;rear = node;}queSize++;}/* 出队 */int pop() {int num = peek();// 删除头节点ListNode *tmp = front;front = front->next;// 释放内存delete tmp;queSize--;return num;}/* 访问队首元素 */int peek() {if (size() == 0)throw out_of_range("队列为空");return front->val;}/* 将链表转化为 Vector 并返回 */vector<int> toVector() {ListNode *node = front;vector<int> res(size());for (int i = 0; i < res.size(); i++) {res[i] = node->val;node = node->next;}return res;}
};
/* 基于环形数组实现的队列 */
class ArrayQueue {private:int *nums;       // 用于存储队列元素的数组int front;       // 队首指针,指向队首元素int queSize;     // 队列长度int queCapacity; // 队列容量public:ArrayQueue(int capacity) {// 初始化数组nums = new int[capacity];queCapacity = capacity;front = queSize = 0;}~ArrayQueue() {     delete[] nums;    }/* 获取队列的容量 */int capacity() {      return queCapacity;    }/* 获取队列的长度 */int size() {        return queSize;    }/* 判断队列是否为空 */bool isEmpty() {      return size() == 0;    }/* 入队 */void push(int num) {if (queSize == queCapacity) {cout << "队列已满" << endl;return;}// 计算队尾指针,指向队尾索引 + 1// 通过取余操作实现 rear 越过数组尾部后回到头部int rear = (front + queSize) % queCapacity;// 将 num 添加至队尾nums[rear] = num;queSize++;}/* 出队 */int pop() {int num = peek();// 队首指针向后移动一位,若越过尾部,则返回到数组头部front = (front + 1) % queCapacity;queSize--;return num;}/* 访问队首元素 */int peek() {if (isEmpty())throw out_of_range("队列为空");return nums[front];}/* 将数组转化为 Vector 并返回 */vector<int> toVector() {// 仅转换有效长度范围内的列表元素vector<int> arr(queSize);for (int i = 0, j = front; i < queSize; i++, j++) {arr[i] = nums[j % queCapacity];}return arr;}
};

应用·:

  • 淘宝订单
  • 各类待办事项

3 双向队列

双向队列(double-ended queue)提供了更高的灵活性,允许在头部和尾部执行元素的添加或删除操作。
常用操作
在这里插入图片描述

/* 初始化双向队列 */
deque<int> deque;
/* 元素入队 */
deque.push_back(2);   // 添加至队尾
deque.push_back(5);
deque.push_back(4);
deque.push_front(3);  // 添加至队首
deque.push_front(1);
/* 访问元素 */
int front = deque.front(); // 队首元素
int back = deque.back();   // 队尾元素
/* 元素出队 */
deque.pop_front();  // 队首元素出队
deque.pop_back();   // 队尾元素出队
/* 获取双向队列的长度 */
int size = deque.size();
/* 判断双向队列是否为空 */
bool empty = deque.empty();

实现

/* 双向链表节点 */
struct DoublyListNode {int val;              // 节点值DoublyListNode *next; // 后继节点指针DoublyListNode *prev; // 前驱节点指针DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) {}
};
/* 基于双向链表实现的双向队列 */
class LinkedListDeque {private:DoublyListNode *front, *rear; // 头节点 front ,尾节点 rearint queSize = 0;              // 双向队列的长度public:/* 构造方法 */LinkedListDeque() : front(nullptr), rear(nullptr) {}/* 析构方法 */~LinkedListDeque() {// 遍历链表删除节点,释放内存DoublyListNode *pre, *cur = front;while (cur != nullptr) {pre = cur;cur = cur->next;delete pre;}}/* 获取双向队列的长度 */int size() {      return queSize;    }/* 判断双向队列是否为空 */bool isEmpty() {       return size() == 0;    }/* 入队操作 */void push(int num, bool isFront) {DoublyListNode *node = new DoublyListNode(num);// 若链表为空,则令 front 和 rear 都指向 nodeif (isEmpty())front = rear = node;// 队首入队操作else if (isFront) {// 将 node 添加至链表头部front->prev = node;node->next = front;front = node; // 更新头节点// 队尾入队操作} else {// 将 node 添加至链表尾部rear->next = node;node->prev = rear;rear = node; // 更新尾节点}queSize++; // 更新队列长度}/* 队首入队 */void pushFirst(int num) {       push(num, true);    }/* 队尾入队 */void pushLast(int num) {        push(num, false);    }/* 出队操作 */int pop(bool isFront) {if (isEmpty())throw out_of_range("队列为空");int val;// 队首出队操作if (isFront) {val = front->val; // 暂存头节点值// 删除头节点DoublyListNode *fNext = front->next;if (fNext != nullptr) {fNext->prev = nullptr;front->next = nullptr;}delete front;front = fNext; // 更新头节点// 队尾出队操作} else {val = rear->val; // 暂存尾节点值// 删除尾节点DoublyListNode *rPrev = rear->prev;if (rPrev != nullptr) {rPrev->next = nullptr;rear->prev = nullptr;}delete rear;rear = rPrev; // 更新尾节点}queSize--; // 更新队列长度return val;}/* 队首出队 */int popFirst() {       return pop(true);    }/* 队尾出队 */int popLast() {    return pop(false);    }/* 访问队首元素 */int peekFirst() {if (isEmpty())throw out_of_range("双向队列为空");return front->val;}/* 访问队尾元素 */int peekLast() {if (isEmpty())throw out_of_range("双向队列为空");return rear->val;}/* 返回数组用于打印 */vector<int> toVector() {DoublyListNode *node = front;vector<int> res(size());for (int i = 0; i < res.size(); i++) {res[i] = node->val;node = node->next;}return res;}
};
/* 基于环形数组实现的双向队列 */
class ArrayDeque {private:vector<int> nums; // 用于存储双向队列元素的数组int front;        // 队首指针,指向队首元素int queSize;      // 双向队列长度public:/* 构造方法 */ArrayDeque(int capacity) {nums.resize(capacity);front = queSize = 0;}/* 获取双向队列的容量 */int capacity() {return nums.size();}/* 获取双向队列的长度 */int size() {return queSize;}/* 判断双向队列是否为空 */bool isEmpty() {return queSize == 0;}/* 计算环形数组索引 */int index(int i) {// 通过取余操作实现数组首尾相连// 当 i 越过数组尾部后,回到头部// 当 i 越过数组头部后,回到尾部return (i + capacity()) % capacity();}/* 队首入队 */void pushFirst(int num) {if (queSize == capacity()) {cout << "双向队列已满" << endl;return;}// 队首指针向左移动一位// 通过取余操作实现 front 越过数组头部后回到尾部front = index(front - 1);// 将 num 添加至队首nums[front] = num;queSize++;}/* 队尾入队 */void pushLast(int num) {if (queSize == capacity()) {cout << "双向队列已满" << endl;return;}// 计算队尾指针,指向队尾索引 + 1int rear = index(front + queSize);// 将 num 添加至队尾nums[rear] = num;queSize++;}/* 队首出队 */int popFirst() {int num = peekFirst();// 队首指针向后移动一位front = index(front + 1);queSize--;return num;}/* 队尾出队 */int popLast() {int num = peekLast();queSize--;return num;}/* 访问队首元素 */int peekFirst() {if (isEmpty())throw out_of_range("双向队列为空");return nums[front];}/* 访问队尾元素 */int peekLast() {if (isEmpty())throw out_of_range("双向队列为空");// 计算尾元素索引int last = index(front + queSize - 1);return nums[last];}/* 返回数组用于打印 */vector<int> toVector() {// 仅转换有效长度范围内的列表元素vector<int> res(queSize);for (int i = 0, j = front; i < queSize; i++, j++) {res[i] = nums[index(j)];}return res;}
};

应用

  • 双向队列兼具栈与队列的逻辑,因此它可以实现这两者的所有应用场景,同时提供更高的自由度。

4 Tips

  1. 浏览器的前进后退功能本质上是“栈”的体现。
  2. 在出栈后,如果后续仍需要使用弹出节点则不需要释放内存,反之则c/c++需要手动释放内存。
  3. 双向队列表现的是栈+队列的逻辑,可实现栈与队列的所有应用,且更灵活。
  4. 撤销(undo)与反撤销(redo)的实现:
    使用两个栈,栈 A 用于撤销,栈 B 用于反撤销。
    每当用户执行一个操作,将这个操作压入栈 A ,并清空栈 B 。
    当用户执行“撤销”时,从栈 A 中弹出最近的操作,并将其压入栈 B 。
    当用户执行“反撤销”时,从栈 B 中弹出最近的操作,并将其压入栈 A 。

————————————————————————————————————————————————————————————
—————————————————————Hello算法—速通笔记—第三集—end—————————————————————–—-

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

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

相关文章

鼠标坐标传感器FCT3065

参考链接 如何优雅的DIY鼠标&#xff1f; | 技术文章 | 汇顶科技开发者社区 (goodix.com)https://developers.goodix.com/zh/bbs/blog_detail/bebdd04ccdfc4f7682ab27a8e77a14ad GitHub - VineetSukhthanker/FCT3065-XY_MouseSensor: Interface FCT3065-XY optical mouse sen…

面试算法准备:动态规划

这里写自定义目录标题 1 理论2 例题2.1 斐波那契数列&#xff08;什么是重叠子问题&#xff09;2.1.1 带备忘录的递归解法 2.2 零钱兑换&#xff08;讲解最优子结构&#xff09;2.3 最长递增子序列&#xff08;讲解如何求解状态转移方程&#xff09;2.4 俄罗斯套娃信封问题&…

Vue3、 Vue2 Diff算法比较

Vue2 Diff算法 源码位置:src/core/vdom/patch.ts 源码所在函数:updateChildren() 源码讲解: 有新旧两个节点数组:oldCh和newCh; 有下面几个变量: oldStartIdx 初始值=0 oldStartVnode 初始值=oldCh[0] oldEndIdx 初始值=oldCh.length - 1 oldEndVnode 初始值=oldCh[ol…

鸿蒙 harmonyos 线程 并发 总结 async promise Taskpool woker(三)多线程并发 Worker

Worker Worker是与主线程并行的独立线程。创建Worker的线程称之为宿主线程&#xff0c;Worker自身的线程称之为Worker线程。创建Worker传入的url文件在Worker线程中执行&#xff0c;可以处理耗时操作但不可以直接操作UI。 Worker主要作用是为应用程序提供一个多线程的运行环境…

CTFshow-PWN-栈溢出(pwn36)

存在后门函数&#xff0c;如何利用&#xff1f; 好好好&#xff0c;终于到了这种有后门函数的了 checksec 检查一下&#xff1a; 32 位程序&#xff0c;RELRO 保护部分开启 RWX: Has RWX segments 存在可读可写可执行的段 使用 ida32 看 main 函数 跟进 ctfshow 函数…

Scala 04 —— Scala Puzzle 拓展

Scala 04 —— Scala Puzzle 拓展 文章目录 Scala 04 —— Scala Puzzle 拓展一、占位符二、模式匹配的变量和常量模式三、继承 成员声明的位置结果初始化顺序分析BMember 类BConstructor 类 四、缺省初始值与重载五、Scala的集合操作和集合类型保持一致性第一部分代码解释第二…

L3-1 夺宝大赛-2024天梯赛(内存超限解决方法)

题目 夺宝大赛的地图是一个由 nm 个方格子组成的长方形&#xff0c;主办方在地图上标明了所有障碍、以及大本营宝藏的位置。参赛的队伍一开始被随机投放在地图的各个方格里&#xff0c;同时开始向大本营进发。所有参赛队从一个方格移动到另一个无障碍的相邻方格&#xff08;“…

聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用

前言 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c;类…

Redis入门到通关之Redis实现Session共享

文章目录 ☃️前期概要☃️基于Session实现登录方案☃️现有方案存在的问题☃️Redis代替Session的业务流程❄️❄️设计key的结构❄️❄️设计key的具体细节❄️❄️整体访问流程 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博…

redis原理篇(黑马程序员虎哥 )回忆笔记

原理&#xff0c;老师讲的真好。相见恨晚。 以下内容是按视频课程的章节安排&#xff0c;在我自己听完课之后&#xff0c;凭借记忆总结的。&#xff08;可能存在疏漏不足&#xff0c;后期补全和修正&#xff0c;同时也在这个过程巩固我自己的对于这个组件相关原理的学习&#x…

中国DIVI版,wordpress DIVI网站主题在国内的替代方案。

最受欢迎的WordPress主题之一是Divi。我们创建了这个全面的Divi主题评论&#xff0c;以帮助您更好地了解其优点和潜在缺点。 Divi主题是什么&#xff1f; Divi是一个流行的WordPress主题&#xff0c;提供了一个网站建设平台。它有一个可视化编辑器选项&#xff0c;为新手和专业…

手撕红黑树(map和set底层结构)(2)

[TOC]红黑树 一 红黑树概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&…

选择ERP系统需要考虑哪些因素 企业ERP系统选型指南

ERP系统是一个复杂的软件系统&#xff0c;中小企业要建成ERP系统首先是要选择一个适合自己的ERP软件。目前市场上的ERP软件品种繁多&#xff0c;功能各异&#xff0c;那么中小企业应如何结合自己的实际情况“量体裁衣”找到最适合自己的ERP软件呢?这是目前中小企业进行ERP选型…

介绍-响应式编程-001

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace 开篇&am…

Ultralytics YOLOv8 英伟达™ Jetson®处理器部署

系列文章目录 前言 本综合指南提供了在英伟达 Jetson设备上部署Ultralytics YOLOv8 的详细攻略。此外&#xff0c;它还展示了性能基准&#xff0c;以证明YOLOv8 在这些小巧而功能强大的设备上的性能。 备注 本指南使用Seeed Studio reComputer J4012进行测试&#xff0c;它基于…

2024年三支一扶报名照上传要求很严格

2024年三支一扶报名照上传要求很严格

Unity 使用GPU计算物体距离

在游戏开发中&#xff0c;计算物体之间的距离是一个常见的需求&#xff0c;例如用于碰撞检测、视觉效果等。传统的计算方法可能会在大量物体时带来性能问题&#xff0c;而在 Unity 中&#xff0c;借助 GPU 进行计算可以有效提高性能。本文将介绍一种使用 Compute Shader 在 Uni…

windows安装nc命令的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

C++ | Leetcode C++题解之第40题组合总和II

题目&#xff1a; 题解&#xff1a; class Solution { private:vector<pair<int, int>> freq;vector<vector<int>> ans;vector<int> sequence;public:void dfs(int pos, int rest) {if (rest 0) {ans.push_back(sequence);return;}if (pos fr…

找回删除的视频,3个神奇小妙招!

“作为一名业余摄影师&#xff0c;我保存了很多重要的视频文件在电脑上&#xff0c;但刚刚电脑突然没电关机&#xff0c;开机后我发现很多视频莫名消失了。有什么方法可以找回这些丢失的视频吗&#xff1f;” 在数字化时代&#xff0c;视频文件往往承载着我们生活中的重要回忆。…