【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
什么是栈?什么是队列?
什么是先进后出?什么是先进先出?
了解基础之后,又如何用来写算法题?
带着这些疑问,让我带领你,走进栈与队列的世界
栈与队列
栈:
1、栈的基本定义:
栈其实就是一种线性表,它只允许在固定的一段进行插入或者删除元素。
你可以把它想象成一个只能从上面开口放东西和拿东西的盒子。
那个能放东西和拿东西的上面开口的地方,我们就叫它栈顶。
2、栈的核心操作:入栈、出栈、取栈顶元素
3、栈的特性:先进后出(LIFO,Last In First Out)
什么意思呢?
就拿枪的弹夹来举例,把(栈)比作弹夹,把(元素)类比为子弹。
当你往弹夹里压子弹的时候,最后压进去的那颗子弹,会在最上面,
等开枪的时候,它会第一个被打出去。
而最开始压进去的子弹,因为被后来的子弹压在下面了,
所以是最后才被打出来的。
这就是先进后出
队列:
1、队列的基本定义:
队列本质上也是一种特殊的线性表。
它就像我们在排队一样,只允许在队伍尾部(队尾)加人(元素),只有队伍的最前排(队首)人(元素)才能离开。
2、队列的核心操作:入队列、出队列、取出队首元素。
3、队列的特性:先进先出(FIFO,First In First Out)
什么意思呢?
就像火车过隧道的例子,火车头就是队首,火车尾就是队尾。
火车进隧道的时候,车头先进入隧道,等出隧道的时候,也是车头先出来,
车尾后进去所以就后出来。
这就跟队列里的元素一样,先进入队列的元素会先出队列,后进入的就后出队列。
深度思索
上方只能让你浅显的了解各个函数的表层含义,它和咱们计算机还是不搭边的。
那他们在计算机中,那些场景会用到这些呢?
栈:
最典型的就是嵌套结构管理
- 括号匹配:遇到 (){} 等符号时,会将左括号,压入栈中,当遇到右括号时,判断栈中括号是否匹配。
- 调用函数:程序调用函数时,会把当前位置压栈保存,等函数执行完毕之后再按逆序返回。
栈在算法中的常见应用
- 表达式求值(中缀转后缀表达式)
- 浏览器后退 / 前进功能
- 迷宫寻路算法(深度优先搜索 DFS)
队列:
- 任务公平调度,多个程序排队等待CPU处理,按顺序执行避免混乱。
- 异步通信桥梁,微信发消息时,消息先入队列,对方手机再按顺序接收,避免同步压力
队列在算法中的常见应用
- 广度优先搜索(BFS)
- 消息队列系统(Kafka/RabbitMQ)
- 缓存淘汰策略(FIFO 算法)
栈与队列的算法实现
栈的实现:
栈最常用的四个函数,放入、弹出、取栈顶元素、判断是否为空
基于顺序表实现栈:
const int max_size = 100;
struct MyStack{
private:int arr[max_size];int size;
public:// 初始化MyStack():size(0){};// 放入void push(int val){if(size>max_size-1){cout<<"已抵达最大容量"<<endl;}arr[size]=val;size++;}// 弹出int pop(){if(empty()){cout<<"暂无数据,弹出失败"<<endl;return 0;}int cur = arr[size-1];size--;return cur;}int top() {if (empty()) {cout << "该栈为空栈" << endl;return 0;}int cur = arr[size-1];return cur;}bool empty(){if(size<=0) return true;else return false;}
};int main(){return 0;
}
基于链表实现栈:
struct Node{
public:int val;Node* next;Node():val(0),next(nullptr){}Node(int val):val(val),next(nullptr){}
};struct MyStack{
private:// 链表头指针Node* head; // 不同的色彩,我也想见一见
public:~MyStack(){while(head){Node* temp = head;head = head->next;delete temp;}}MyStack():head(nullptr){}// 入栈void push(int val){Node* cur = new Node(val);cur->next = head;head = cur;}// 出栈int pop(){if(empty()){cout<<"空栈"<<endl;return 0;}Node* temp = head;int cur = temp->val;head = head->next;delete temp;return cur;}// 取元素int top(){if(empty()){cout<<"空栈"<<endl;return 0;}return head->val;}// 判断是否为空bool empty(){return head == nullptr;}};
队列的实现:
基于顺序表实现队列
// 基于数组,实现的循环队列
const int CAPACITY = 100;
struct MyQueue{
private:int data[CAPACITY];int head;int tail;int count;
public:MyQueue():head(0),tail(0),count(0){}// 入队列void push(int val){if(count>=CAPACITY){cout<<"队列已满"<<endl;return;}data[tail] = val;tail = (tail+1)%CAPACITY;count+=1;}// 出队列int pop(){if(empty()){cout<<"空队列"<<endl;return 0;}int cur = data[head];head=(head+1)%CAPACITY;count--;return cur;}// 取队首元素int top(){if(empty()){cout<<"空队列"<<endl;return 0;}int cur = data[head];return cur;}// 判断是否为空bool empty(){return !count;}
};
基于链表实现队列
#include <iostream>// 基于链表来实现队列
class MyQueue{
private:// 定义链表节点的结构体struct Node {int val; // 节点存储的值Node* next; // 指向下一个节点的指针// 节点的构造函数,用于初始化节点的值Node(int val) : val(val), next(nullptr) {}};Node* newHead; // 链表的头节点指针Node* newTail; // 链表的尾节点指针public:// 队列的构造函数,初始化头节点和尾节点指针为 nullptr,表示队列为空MyQueue() : newHead(nullptr), newTail(nullptr) {}// 析构函数,用于释放链表中所有节点的内存,防止内存泄漏~MyQueue() {while (newHead) {Node* temp = newHead;newHead = newHead->next;delete temp;}}// 1. 入队列,就是尾插,为了和 Java 库中的队列保持一致,用 bool 返回值bool offer(int val) {// 创建一个新的节点,存储要插入的值Node* newNode = new Node(val);// 特殊情况处理:如果链表为空if (newHead == nullptr) {// 新节点既是头节点也是尾节点newHead = newNode;newTail = newNode;} else {// 一般情况处理:将新节点插入到链表尾部newTail->next = newNode;// 更新尾节点指针指向新的尾节点newTail = newTail->next;}return true;}// 2. 出队列,就是头删,注意头删也是要返回那个要删除的值int poll() {// 特殊情况处理:链表为空,没得删if (newHead == nullptr) {std::cout << "队列为空,无法出队" << std::endl;return -1; // 这里用 -1 表示出队失败,可根据实际情况修改}// 保存要出队的值int ret = newHead->val;Node* temp = newHead;// 链表只有一个元素的情况if (newHead->next == nullptr) {// 出队后链表为空,头节点和尾节点都置为 nullptrnewHead = nullptr;newTail = nullptr;} else {// 一般情况处理:更新头节点指针指向下一个节点newHead = newHead->next;}// 释放被删除节点的内存delete temp;return ret;}// 3. 取队列首元素int top() {// 特殊情况处理:链表为空,没得取if (newHead == nullptr) {std::cout << "队列为空,无法获取队首元素" << std::endl;return -1; // 这里用 -1 表示获取失败,可根据实际情况修改}// 一般情况处理:返回头节点的值return newHead->val;}
};
竞赛中如何运用
其实这才是很多人关心的问题!毕竟学了,就是为了用!
但是怎么用呢?
总不能每次用到栈和队列时,都手敲实现吧?那太抽象了点!
所以,我们这里就引入了STL库中的<stack>与<queue>两个函数库
先简单的说一下,拓展一下认知:
栈与队列 是 以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈与队列的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
那么问题来了,STL 中栈是用什么容器实现的?
从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
<stack> /stæk/
- 栈顶插入(push)
- 栈顶弹出(pop)
- 栈顶获取元素(top)
- 判断是否为空(empty)
#include <iostream>
#include <stack>
using namespace std;int main(){stack<int> s1;s1.push(1);s1.push(2);s1.pop();cout<<s1.top();while(!s1.empty()){cout<<s1.top();s1.pop();}cout<<endl;// ==== 作为容器适配器 ====stack<int,deque<int>> s2;s2.push(1);s2.push(2);s2.pop();cout<<s2.top();while(!s2.empty()){cout<<s2.top();s2.pop();}return 0;
}
<queue> /kjuː/
- 插入(push)
- 删除(pop)
- 获取队首(front)
- 获取队尾(back)
- 判断非空(empty)
#include <queue>
#include <iostream>
using namespace std;
int main(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);// 首cout<<q1.front()<<endl;// 尾cout<<q1.back()<<endl;// 循环取出while(!q1.empty()){cout<<q1.front()<<endl;q1.pop();}return 0;
}
大纲
基础
一、用栈实现队列-(解析)-基础
二、用队列实现栈-(解析)-基础
三、有效的括号-(解析)-栈的基础应用
四、删除字符串中的所有相邻重复项-(解析)-栈的基础应用
五、 逆波兰表达式求值-(解析)-栈的基础应用
六、滑动窗口最大值-(解析)-单调栈的基本应用
七、前 K 个高频元素-(解析)-
蓝桥真题
一、买二赠一-(解析)-将队列做为辅助空间
题目
基础练习
一、用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push、pop、peek、empty):实现
MyQueue类:
void push(int x)将元素 x 推到队列的末尾int pop()从队列的开头移除并返回元素int peek()返回队列开头的元素boolean empty()如果队列为空,返回true;否则,返回false说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top,peek/pop from top,size, 和is empty操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 1, 1, false]解释: MyQueue myQueue = new MyQueue(); myQueue.push(1); // queue is: [1] myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue) myQueue.peek(); // return 1 myQueue.pop(); // return 1, queue is [2] myQueue.empty(); // return false提示:
1 <= x <= 9- 最多调用
100次push、pop、peek和empty- 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop或者peek操作)
class MyQueue {
private:stack<int> s1;stack<int> s2;
public:// 推入void push(int val){s1.push(val);}// 交换函数void Swap(stack<int>& s1, stack<int>& s2){while(!s1.empty()){s2.push(s1.top());s1.pop();}}// 移除int pop(){Swap(s1,s2);int cur = s2.top();s2.pop();Swap(s2,s1);return cur;}// 返回开头元素int peek(){Swap(s1,s2);int cur = s2.top();Swap(s2,s1);return cur;}// 判断非空bool empty(){return s1.empty();}};/*** Your MyQueue object will be instantiated and called as such:* MyQueue* obj = new MyQueue();* obj->push(x);* int param_2 = obj->pop();* int param_3 = obj->peek();* bool param_4 = obj->empty();*/
二、用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(
push、top、pop和empty)。实现
MyStack类:
void push(int x)将元素 x 压入栈顶。int pop()移除并返回栈顶元素。int top()返回栈顶元素。boolean empty()如果栈是空的,返回true;否则,返回false。注意:
- 你只能使用队列的标准操作 —— 也就是
push to back、peek/pop from front、size和is empty这些操作。- 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入: ["MyStack", "push", "push", "top", "pop", "empty"] [[], [1], [2], [], [], []] 输出: [null, null, null, 2, 2, false]解释: MyStack myStack = new MyStack(); myStack.push(1); myStack.push(2); myStack.top(); // 返回 2 myStack.pop(); // 返回 2 myStack.empty(); // 返回 False提示:
1 <= x <= 9- 最多调用
100次push、pop、top和empty- 每次调用
pop和top都保证栈不为空
class MyStack {
private:queue<int> q1;queue<int> q2;
public:// 压入元素void push(int val){q1.push(val);}// 交换元素void Swap(queue<int>& q1, queue<int>& q2){while(q1.size()>1){q2.push(q1.front());q1.pop();}}// 移除元素int pop(){ // 移除元素Swap(q1,q2);int val = q1.front();q1.pop();Swap(q2,q1);if(q2.size()!=0){q1.push(q2.front());q2.pop();}return val;}// 取出元素int top(){Swap(q1,q2);int val = q1.front();q2.push(val);q1.pop();Swap(q2,q1);if(q2.size()!=0){q1.push(q2.front());q2.pop();}return val;}// 判断非空bool empty(){return q1.empty();}};/*** Your MyStack object will be instantiated and called as such:* MyStack* obj = new MyStack();* obj->push(x);* int param_2 = obj->pop();* int param_3 = obj->top();* bool param_4 = obj->empty();*/
三、有效的括号
给定一个只包括
'(',')','{','}','[',']'的字符串s,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([])"
输出:true
提示:
1 <= s.length <= 104s仅由括号'()[]{}'组成
class Solution {// 解决符号问题,就是stack的专场// 考虑的不够周全/*3种特殊情况右括号的情况:1、右括号的左边不是对应括号2、出现右括号,但是stack为空意外情况:1、字符串遍历完,仍然还有符号*/
public:bool isValid(string s) {stack<int> myStack;for(char c : s){if(c=='('||c=='['||c=='{') myStack.push(c);else{if(myStack.size()==0) return false;else {char ch = myStack.top();if(ch=='('&&c==')') myStack.pop();else if(ch=='['&&c==']') myStack.pop();else if(ch=='{'&&c=='}') myStack.pop();else return false;}}}if(myStack.size()!=0) return false;return true;}
};
四、删除字符串中的所有相邻重复项
给出由小写字母组成的字符串
s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在
s上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca" 输出:"ca" 解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。提示:
1 <= s.length <= 105s仅由小写英文字母组成。
class Solution {// 相邻匹配问题,stack的专场stack<char> myStack;
public:string removeDuplicates(string s) {for(char c : s){if(!myStack.empty() && myStack.top()==c) myStack.pop();else myStack.push(c);}string str="";while(!myStack.empty()){str+=myStack.top();myStack.pop();}reverse(str.begin(),str.end());return str;}
};
五、逆波兰表达式求值
给你一个字符串数组
tokens,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'、'-'、'*'和'/'。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] 输出:22 解释:该算式转化为常见的中缀算术表达式为:((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22提示:
1 <= tokens.length <= 104tokens[i]是一个算符("+"、"-"、"*"或"/"),或是在范围[-200, 200]内的一个整数逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )。- 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )。逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *也可以依据次序计算出正确结果。- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
class Solution {// 栈是符号表达式的专场stack<long long> s;
public:int evalRPN(vector<string>& tokens) {for(string str : tokens){if(str=="+"||str=="-"||str=="*"||str=="/"){int num2 = s.top(); s.pop();int num1 = s.top(); s.pop();if(str=="+") s.push(num1+num2);else if(str=="-") s.push(num1-num2);else if(str=="*") s.push(num1*num2);else s.push(num1/num2);}else s.push(stoll(str));}return s.top();}
};
六、滑动窗口最大值
给你一个整数数组
nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 31 [3 -1 -3] 5 3 6 7 31 3 [-1 -3 5] 3 6 7 51 3 -1 [-3 5 3] 6 7 51 3 -1 -3 [5 3 6] 7 61 3 -1 -3 5 [3 6 7] 7示例 2:
输入:nums = [1], k = 1 输出:[1]提示:
1 <= nums.length <= 105-104 <= nums[i] <= 1041 <= k <= nums.length
class Solution {
private:struct myQueue{ // 手搓,单调队列 vprivate:deque<int> myDeque;public:void push(int val){ // 放while(!myDeque.empty()&&myDeque.back()<val) myDeque.pop_back();myDeque.push_back(val);}void pop(int val){ // 弹出if(!myDeque.empty() && val==myDeque.front()) myDeque.pop_front();}int front(){ // 输出if(!myDeque.empty()) return myDeque.front();return 0;}};
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> vec;myQueue mq;for(int i=0; i<k; ++i) mq.push(nums[i]); // 存入vec.push_back(mq.front()); for(int i=k; i<nums.size(); ++i){mq.pop(nums[i-k]);mq.push(nums[i]);int val = mq.front();vec.push_back(mq.front());}return vec;}
};
七、前 K 个高频元素
给你一个整数数组
nums和一个整数k,请你返回其中出现频率前k高的元素。你可以按 任意顺序 返回答案。示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]示例 2:
输入: nums = [1], k = 1 输出: [1]提示:
1 <= nums.length <= 105k的取值范围是[1, 数组中不相同的元素的个数]- 题目数据保证答案唯一,换句话说,数组中前
k个高频元素的集合是唯一的进阶:你所设计算法的时间复杂度 必须 优于
O(n log n),其中n是数组大小。
哒哒哒,后续补充
蓝桥真题
一、买二赠一
问题描述
某商场有 NN 件商品,其中第 ii 件的价格是 AiAi。现在该商场正在进行 “买二赠一” 的优惠活动,具体规则是:每购买 22 件商品,假设其中较便宜的价格是 PP(如果两件商品价格一样,则 PP 等于其中一件商品的价格),就可以从剩余商品中任选一件价格不超过 P22P 的商品,免费获得这一件商品。可以通过反复购买 22 件商品来获得多件免费商品,但是每件商品只能被购买或免费获得一次。
小明想知道如果要拿下所有商品(包含购买和免费获得),至少要花费多少钱?
输入格式
第一行包含一个整数 NN。
第二行包含 NN 个整数,代表 A1,A2,A3,…,ANA1,A2,A3,…,AN。
输出格式
输出一个整数,代表答案。
样例输入
7 1 4 2 8 5 7 1样例输出
25样例说明
小明可以先购买价格 44 和 88 的商品,免费获得一件价格为 11 的商品;再后买价格为 55 和 77 的商品,免费获得价格为 22 的商品;最后单独购买剩下的一件价格为 11 的商品。总计花费 4+8+5+7+1=254+8+5+7+1=25。不存在花费更低的方案。
评测用例规模与约定
对于 3030% 的数据,1≤N≤201≤N≤20。
对于 100100% 的数据,1≤N≤5×1051≤N≤5×105,1≤Ai≤1091≤Ai≤109。
Java版:
import java.util.*;
// 重点在于Queue这种辅助空间,不易想到
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取输入的元素个数int n = scanner.nextInt();// 创建一个长整型数组来存储输入的元素long[] vec = new long[n];for (int i = 0; i < n; i++) {// 读取每个元素并存储到数组中vec[i] = scanner.nextLong();}// 对数组进行降序排序Arrays.sort(vec);reverseArray(vec);// 创建一个队列来存储处理后的元素Queue<Long> que = new LinkedList<>();int k = 0;long sum = 0;// 遍历排序后的数组for (int i = 0; i < n; i++) {// 如果队列不为空且队列头部元素大于等于当前数组元素if (!que.isEmpty() && que.peek() >= vec[i]) {// 移除队列头部元素que.poll();continue;}// 如果 k 小于 2if (k < 2) {k++;// 累加当前数组元素到总和中sum += vec[i];}// 如果 k 大于等于 2if (k >= 2) {// 将当前数组元素除以 2 后加入队列que.add(vec[i] / 2);k = 0;}}// 输出总和System.out.println(sum);scanner.close();}// 反转数组元素顺序以实现降序排序public static void reverseArray(long[] arr) {int left = 0;int right = arr.length - 1;while (left < right) {long temp = arr[left];arr[left] = arr[right];arr[right] = temp;left++;right--;}}
}
C++版:
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#define ll long long
using namespace std;
// 重点在于,queue这种辅助空间,不会轻易被想到
bool cmp(ll a, ll b){return a>b;
}
int main()
{// 理智,一点int n;cin>>n;vector<ll> vec(n);for(int i=0; i<n; ++i) cin>>vec[i];sort(vec.begin(),vec.end(),cmp); // 排序queue<ll> que;int k=0;ll sum=0;for(int i=0; i<n; ++i){if(!que.empty()&&que.front()>=vec[i]){que.pop();continue;}if(k<2){k++;sum+=vec[i];}if(k>=2){que.push(vec[i]/2);k=0;}}cout<<sum<<endl;return 0;
}
知识点
1、析构函数的作用
- 自动调用:其作用是,在对象生命周期结束时自动释放资源,防止资源泄露。
- 怎么调用:类名(){...}前加上~,代表析构。
- 默认实现:未被显示定义,编辑器会自动生成一个空析构函数。
2、C++标准库<deque> /dek/
deque /dek/ 是标准模版库(STL)的一部分,他提供了双端队列(double-ended queue)的实现。
是一种允许,在两端进行插入和删除操作的线性数据结构。
并且<deque>的随机访问时间复杂度为O(1)。
不过deque 是由多个连续的存储块组成的,这些存储块在内存中不一定是连续的。只是随机访问的话,还是vector(头插O(n),尾插O(1))比较合适。
#include <iostream>
#include <deque>
using namespace std;
int main(){deque<int> myDeque;// 插入myDeque.push_back(3);myDeque.push_back(4);myDeque.push_front(2);myDeque.push_front(1);// 访问元素for(int i=0; i<myDeque.size(); ++i){cout<<myDeque[i]<<endl;}// 删除元素myDeque.pop_back();myDeque.pop_front();// 访问头部与尾部元素cout<<myDeque.front()<<endl;cout<<myDeque.back()<<endl;return 0;
}
借鉴博客:
1、数据结构:栈和队列(Stack & Queue)【详解】
2、栈和队列(详细版,一看就懂。包含栈和队列的定义、意义、区别,实现)
3、栈与队列理论基础
4、C++ 标准库 <stack>
5、C++ 容器类 <queue>
相关文章:
【数据结构】栈与队列:基础 + 竞赛高频算法实操(含代码实现)
什么是栈?什么是队列? 什么是先进后出?什么是先进先出? 了解基础之后,又如何用来写算法题? 带着这些疑问,让我带领你,走进栈与队列的世界 栈与队列 栈: 1、栈的基本…...
CSP-J/S冲奖第18天:真题解析
解题步骤 读取输入:首先读取整数n,然后读取n个正整数并存储在一个数组或容器中。 排序数组:对数组进行排序,以便后续使用双指针法高效查找。 遍历数组:对于每个数target,检查是否存在另外两个不同的数a和…...
【linux】虚拟机执行sudo yum isntall perl报错 could not retrieve mirrorlist htt:
项目场景: 提示:虚拟机安装拓展包,sudo yum install perl Virtualbox 在不安装增强功能扩展的情况下, 无法自适应分辨率和共享剪切板等操作 问题描述 原因分析: 提示:这里填写问题的分析: 出现这个错误是因…...
旅游类小程序界面设计
产品概述 艾啦游是一款互联网旅游类小程序,致力于国内精品旅游,以及拥有自由行、专属热榜单、出行攻略等诸多功能,汇聚了许多国内的人气景点,与诸多城市的酒店也保持合作,打造一体式旅行服务,更有不断上新…...
DQN 玩 2048 实战|第三期!优化网络,使用GPU、Env奖励优化
视频讲解: DQN 玩 2048 实战|第三期!优化网络,使用GPU、Env奖励优化 1. 仅考虑局部合并奖励:目前的奖励只设置为合并方块时获得的分数,只关注了每一步的即时合并收益,而没有对最终达成 2048 这个…...
【python】http post 在body中传递json数据 以发送
http post 在body中传递json数据 以发送,json的格式非常重要这里要传递json对象,而不是一个json字符串 传递post一个 JSON 字符串 是ok的 是的, {"rsource_rhythm_action_list": {"name": "AI_\\u6708\\u4eae\\u…...
Linux错误(2)程序触发SIGBUS信号分析
Linux错误(2)之SIGBUS错误分析 Author: Once Day Date: 2025年3月12日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践记录_Once_day的博…...
【Halcon】灰度不均解决方案
目录 1、平场校正 2、形态学背景估计 3、频域滤波抑制低频光照不均 4、动态局部自适应 1、平场校正 原理:通过白场(White Image)和黑场(Black Image)图像,手动计算校正系数 * 读取图像 read_image(ImageRaw, raw_image) // 原始图像 read_image(ImageWhite, …...
滑动窗口算法详解:从入门到精通
目录 引言 1. 滑动窗口算法简介 2. 滑动窗口的基本思想 3. 滑动窗口的应用场景 3.1 最大子数组和 3.2 最小覆盖子串 3.3 最长无重复字符子串 4. 滑动窗口的实现步骤 5. 滑动窗口的代码示例 6. 滑动窗口的优化技巧 6.1 使用哈希表记录字符频率 6.2 使用双指针维护窗口…...
JAVA数据库技术(一)
JDBC 简介 JDBC(Java Database Connectivity)是Java平台提供的一套用于执行SQL语句的Java API。它允许Java程序连接到数据库,并通过发送SQL语句来查询、更新和管理数据库中的数据。JDBC为不同的数据库提供了一种统一的访问方式,使…...
LightGBM + TA-Lib A股实战进阶:Optuna调优与Plotly可视化详解
LightGBM TA-Lib A 股实战进阶:Optuna 调优与 Plotly 可视化详解 本文系统讲解了 LightGBM 在 A 股市场的应用,涵盖模型构建、Optuna 参数调优及 Plotly 可视化。通过实战案例,帮助读者全面掌握相关技术,提升在金融数据分析与预测…...
第二:go 链接mysql 数据库
mac mysql 安装 的步骤 mysql 安装 配制: https://juejin.cn/post/7454870544929472550 mac brew 如何安装mysql数据库 要在Mac上使用Homebrew安装MySQL数据库,请按照以下步骤操作:步骤 1: 安装Homebrew 如果你还没有安装Homebrew&a…...
QListView、QListWidget、QTableView和QTableWidget
一、概念 在Qt框架中,QListView、QListWidget、QTableView和QTableWidget都是用于显示列表或表格数据的控件。 QListView是一个基于模型-视图架构的控件,用于展示列表形式的数据。它本身并不存储数据,而是依赖于一个QAbstractListModel或其子…...
[贪心算法]-最大数(lambda 表达式的补充)
1.解析 我们一般使用的排序比较大小都是 a>b 那么a在b的前面 ab 无所谓 a<b a在b的后面 本题的排序则是 ab>ba 那么a在b的前面 abba 无所谓 ab<ba a在b的后面 2.代码 class Solution { public:string largestNumber(vector<int>& nums) {//1.先把所有…...
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷二)
目录 1. 数组名与地址 2. 指针访问数组 3.一维数组传参本质 4.二级指针 5. 指针数组 6. 指针数组模拟二维数组 1. 数组名与地址 我们先看下面这个代码: int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p &arr[0]; 这里我们使用 &arr[0] 的方式拿到了数…...
python实现简单的图片去水印工具
python实现简单的图片去水印工具 使用说明: 点击"打开图片"选择需要处理的图片 在图片上拖拽鼠标选择水印区域(红色矩形框) 点击"去除水印"执行处理 点击"保存结果"保存处理后的图片 运行效果 先简要说明…...
使用dify+deepseek部署本地知识库
使用difydeepseek部署本地知识库 一、概述二、安装windows docker desktop1、确认系统的Hyper-v功能正常启用2、docker官网下载安装windows客户端3、安装完成后的界面如下所示 三、下载安装ollama四、部署本地deepseek五、本地下载部署dify5.1 下载dify的安装包5.2 将dify解压到…...
(C语言)指针与指针数组的使用教学(C语言基础教学)(指针教学)
指针是什么?指针怎么用?指针数组又是什么??? 想必大家刚学C语言的时候对指针可谓是十分头疼了,听也听不懂,用也不会用 下面我来用我的理解来教你指针怎么用,还你一个脑子 1.指针的…...
【算法day13】最长公共前缀
最长公共前缀 https://leetcode.cn/problems/longest-common-prefix/submissions/612055945/ 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 “”。 class Solution { public:string longestCommonPrefix(vector<string&g…...
Effective C++ 剖析(条款1~9)
目录 条款01 视C为一个语言联邦(C由几部分组成) 条款02 尽量以 const,enum,inline 替换 #define 条款03 尽量使用 const 条款04 确定对象再使用前已经被初始化 条款05 了解c默默编写并调用那些函数 条款06 若不想使用编译器自动生成的函数就该明确拒绝 条款07 为多态基类…...
【Maven-plugin】有多少官方插件?
之前疏理了容器底层原理,现在回归主题,在阅读 next-public时发现 parent 将从多基础插件集成到 parent 仓库中单独维护,数量众多,故在此将所有插件分类整理。以达观其全貌,心中有数。 以下是 Apache Maven 官方维护的核心插件列表…...
Java高频面试之集合-13
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:为什么 hash 函数能降哈希碰撞? 哈希函数通过以下核心机制有效降低碰撞概率,确保不同输入尽可能映…...
RGV调度算法(三)--遗传算法
1、基于时间窗 https://wenku.baidu.com/view/470e9fd8b4360b4c2e3f5727a5e9856a57122693.html?_wkts_1741880736197&bdQuery%E7%8E%AF%E7%A9%BF%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95 2.2019年MathorCup高校数学建模挑战赛B题 2019-mathorcupB题-环形穿梭机调度模型&a…...
【数学建模】一致矩阵的应用及其在层次分析法(AHP)中的性质
一致矩阵在层次分析法(AHP)中的应用与性质 在层次分析法(AHP)中,一致矩阵是判断矩阵的一种理想状态,它反映了决策者判断的完全合理性和一致性,也就是为了避免决策者认为“A比B重要,B比C重要,但是C又比A重要”的矛盾。…...
YOLOv8轻量化改进——Coordinate Attention注意力机制
现在针对YOLOv8的架构改进越来越多,今天尝试引入了Coordinate Attention注意力机制以改进对小目标物体的检测效率。 yolov8的下载和安装参考我这篇博客: 基于SeaShips数据集的yolov8训练教程_seaships处理成yolov8-CSDN博客 首先我们可以去官网找到CA注…...
php开发转go的学习计划及课程资料信息
以下是为该课程体系整理的配套教材和教程资源清单,包含书籍、视频、官方文档和实战项目资源,帮助你系统化学习: Go语言学习教材推荐(PHP开发者适配版) 一、核心教材(按学习阶段分类) 1. 基础语法阶段(阶段一) 资源类型名称推荐理由链接/获取方式官方教程Go语言之旅交…...
解释 TypeScript 中的枚举(enum),如何使用枚举定义一组常量?
枚举(Enum) 是 TypeScript 中用于定义一组具名常量的核心类型,通过语义化的命名提升代码可读性,同时利用类型检查减少低级错误。 以下从定义方式、使用建议、注意事项三方面深入解析。 一、枚举的定义方式 1. 数字枚举 特性&…...
基于SpringBoot+Vue的驾校预约管理系统+LW示例参考
1.项目介绍 系统角色:管理员、普通用户、教练功能模块:用户管理、管理员管理、教练管理、教练预约管理、车辆管理、车辆预约管理、论坛管理、基础数据管理等技术选型:SpringBoot,Vue等测试环境:idea2024,j…...
ONNX:统一深度学习工作流的关键枢纽
引言 在深度学习领域,模型创建与部署的割裂曾是核心挑战。不同框架训练的模型难以在多样环境部署,而 ONNX(Open Neural Network Exchange)作为开放式神经网络交换格式,搭建起从模型创建到部署的统一桥梁,完…...
蓝桥杯————23年省赛 ——————平方差
3.平方差 - 蓝桥云课 一开始看题我还没有意识到问题的严重性 我丢,我想 的是用两层循环来做,后来我试了一下最坏情况,也就是l1 r 1000000000 结果运行半天没运行出来,我就知道坏了,孩子们,要出事&#…...
