【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://www.rhkb.cn/news/313788.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode in Python 48. Rotate Image/Matrix (旋转图像/矩阵)

旋转图像/矩阵的重点是寻找旋转前后对应位置的坐标关系。 示例&#xff1a; 图1 旋转图像/矩阵的输入输出示意图 代码&#xff1a; class Solution:def rotate(self, matrix):n len(matrix)for i in range(n // 2):for j in range(i, n - 1 - i):topleft matrix[i][j]ma…

Navicat连接SQLSever报错:[08001] MicrosoftTCP Provider 远程主机强迫关闭了一个现有的连接

Navicat连接SQLSever报错&#xff1a;[08001] [Microsoft][SQL Server Native Client 10.0]TCP Provider: 远程主机强迫关闭了一个现有的连接 问题分析 旧版的MSSQL 如果不是最新版的&#xff0c;可以去这安装以下即可。 最新版的MSSQL 如果是安装最新版的MSSQL连接不上很正…

鸿蒙OpenHarmony【轻量系统 环境搭建】 (基于Hi3861开发板)

安装Hi3861开发板特有环境 除上述[安装库和工具集]和[安装编译工具]外&#xff0c;针对Hi3861开发板还需要安装特定的编译工具。 工具要求 表1 Hi3861 WLAN模组需要安装的编译工具 开发工具用途SCons3.0.4编译构建工具python模块&#xff1a;setuptools、kconfiglib、pycry…

Macs Fan Control Pro for Mac:全面优化Mac风扇控制软件

Macs Fan Control Pro for Mac是一款专为苹果电脑用户设计的风扇控制软件&#xff0c;旨在通过精确的风扇速度调节&#xff0c;全面优化Mac的散热性能&#xff0c;确保系统始终运行在最佳状态。 Macs Fan Control Pro for Mac中文版下载 该软件具备实时监控功能&#xff0c;能够…

java 词法分析练习

import parser.Parser;import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class Main {public static void main(String[] args) {// 关键词List<String> keyList new ArrayList<>(Arrays.asList("int","String…

利用STM32 HAL库实现USART串口通信,并通过printf重定向输出“Hello World“

一、开发环境 硬件&#xff1a;正点原子探索者 V3 STM32F407 开发板 单片机&#xff1a;STM32F407ZGT6 Keil版本&#xff1a;5.32 STM32CubeMX版本&#xff1a;6.9.2 STM32Cube MCU Packges版本&#xff1a;STM32F4 V1.27.1 上一篇使用STM32F407的HAL库只需1行代码实现US…

AI大模型探索之路-训练篇1:大语言模型微调基础认知

文章目录 前言一、微调技术概述二、微调的必要性三、大模型的微调方法四、微调过程中的技术细节五、微调后的模型评估与应用总结 前言 在人工智能的广阔研究领域内&#xff0c;大型预训练语言模型&#xff08;Large Language Models, LLMs&#xff09;已经成为推动技术革新的关…

龙芯中标麒麟系统打包生成rpm包步骤

1、检查系统是否安装 rpmbuild 和 rpmdevtools 直至rpmdev-setuptree命令执行成功&#xff0c;执行成功后&#xff0c;此时会在用户根目录下创建文件rpmbuild打包文件夹。 rpmbuild为打包工作目录&#xff0c;结构如下&#xff1a; 解释目录结构&#xff1a; BUILD&#xff1…

Windows安装ElasticSearch

Windows安装ElasticSearch 安装完之后 ElasticSearch服务的段括号是9200&#xff0c;可以直接通过localhost:9200在浏览器里面访问 如下图&#xff1a; 而Kibana&#xff0c;也就是ElasticSearch的客户端的端口号是5601&#xff0c;我们可以直接通过localhost:5601访问 如下…

javaEE初阶——多线程(八)——常见的锁策略 以及 CAS机制

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享分治算法关于多线程进阶的章节——关于常见的锁策略以及CAS机制 如果有不足的或者错误的请您指出! 目录 多线程进阶1.常见的锁策略1.1乐观锁和悲观锁1.2重量级锁 和 轻量级锁1.…

Pytorch 学习路程 - 1:入门

目录 下载Pytorch 入门尝试 几种常见的Tensor Scalar Vector Matrix AutoGrad机制 线性回归尝试 使用hub模块 Pytorch是重要的人工智能深度学习框架。既然已经点进来&#xff0c;我们就详细的介绍一下啥是Pytorch PyTorch 希望将其代替 Numpy 来利用 GPUs 的威力&…

云赛道---AI开发框架

MindSpore 旨在提供端边云全场景的 AI 框架。 MindSpore 可部署于端、边、云不同的 硬件环境&#xff0c;满足不同环境的差异化需求&#xff0c;如支持端侧的轻量化部署&#xff0c;支持云侧丰富的 训练功能如自动微分、混合精度、模型易用编程等。 MindSpore 全场景的几个重…

IIR滤波器的设计与实现(内含设计IIR滤波器的高效方法)

写在前面&#xff1a;初学者学习这部分内容&#xff0c;要直接上手写代码可能会感到比较困难&#xff0c;我这里推荐一种高效快速的设计IIR,FIR滤波器的方法——MATLAB工具箱&#xff1a;filterDesigner。打开的方法很简单&#xff0c;就是在命令行键入&#xff1a;filterDesig…

Visual Studio安装MFC开发组件

MFC由于比较古老了&#xff0c;Visual Studio默认没有这个开发组件。最近由于一些原因&#xff0c;需要使用这个库&#xff0c;这就需要另外安装。 参考了网上的一些资料&#xff0c;根据实际使用&#xff0c;其实很多步骤不是必须的。 https://zhuanlan.zhihu.com/p/68117276…

TypeScript 装饰器

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:TypeScript 装饰器 目录 一、是什么 二、使用方式 类装饰 方法/属性装饰 参数装饰 访问器…

Objective-C网络数据捕获:使用MWFeedParser库下载Stack Overflow示例

概述 Objective-C开发中&#xff0c;网络数据捕获是一项常见而关键的任务&#xff0c;特别是在处理像RSS源这样的实时网络数据流时。MWFeedParser库作为一个优秀的解析工具&#xff0c;提供了简洁而强大的解决方案。本文将深入介绍如何利用MWFeedParser库&#xff0c;以高效、…

深度学习系列64:数字人wav2lip详解

1. 整体流程 第一步&#xff0c;加载视频/图片和音频/tts。用melspectrogram将wav文件拆分成mel_chunks。 第二步&#xff0c;调用face_detect模型&#xff0c;给出人脸检测结果&#xff08;可以改造成从文件中读取&#xff09;&#xff0c;包装成4个数组batch&#xff1a;img…

ExcelVBA把当前工作表导出为PDF文档

我们先问问Kimi Excel导出为PDF的方法有多种&#xff0c;以下是一些常见的方法&#xff1a; 1 使用Excel软件的内置功能&#xff1a; 打开Excel文件&#xff0c;点击“文件”菜单。 选择“另存为”&#xff0c;在“保存类型”中选择“PDF”。 设置保存路径和文件名&#xff…

transformer 最简单学习3, 训练文本数据输入的形式

1、输入数据中&#xff0c;源数据和目标数据的定义 def get_batch(source,i):用于获取每个批数据合理大小的源数据和目标数据参数source 是通过batchfy 得到的划分batch个 ,的所有数据&#xff0c;并且转置列表示i第几个batchbptt 15 #超参数&#xff0c;一次输入多少个ba…

GPU深度学习环境搭建:Win10+CUDA 11.7+Pytorch1.13.1+Anaconda3+python3.10.9

1. 查看显卡驱动及对应cuda版本关系 1.1 显卡驱动和cuda版本信息查看方法 在命令行中输入【nvidia-smi】可以当前显卡驱动版本和cuda版本。 根据显示,显卡驱动版本为:Driver Version: 516.59,CUDA 的版本为:CUDA Version 11.7。 此处我们可以根据下面的表1 显卡驱动和c…