【小菜鸡刷题记】---栈与队列篇

news/2024/11/18 3:46:26/

【小菜鸡刷题记】---栈与队列篇

  • 用两个栈模拟一个队列
  • 用两个队列实现栈
  • 剑指 Offer 58 - I. 翻转单词顺序
  • 剑指 Offer 59 - I. 滑动窗口的最大值
  • 剑指 Offer 30. 包含min函数的栈
  • 剑指 Offer 59 - II. 队列的最大值(max函数的队列)

特此声明:本篇文章题目均来自于力扣

用两个栈模拟一个队列

【题目链接】
将一个栈当作输入栈,用于压入 appendTail
传入的数据;另一个栈当作输出栈,用于 deleteHead操作。

每次 deleteHead 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

class CQueue {
public:CQueue() {}void appendTail(int value) {pushS.push(value);}int deleteHead(){if(popS.empty()){if(pushS.empty()){return -1;}while(!pushS.empty()){int top=pushS.top();pushS.pop();popS.push(top);}}int Del=popS.top();popS.pop();return Del;}stack<int> pushS;//进数据stack<int> popS;//出数据
};

用两个队列实现栈

【题目链接】
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

方案一
通过不断交换两个队列中的值保证其中一个队列中有我们想要弹出的数据

class MyStack {
public:queue<int>  q1;queue<int> q2;MyStack() {}void push(int x) {if(q1.empty() && q2.empty()){q1.push(x);return;}else if(!q1.empty()){q1.push(x);}else if(!q2.empty()){q2.push(x);}}int pop() {int Pop;if(q2.empty()){while(q1.size()>1){q2.push(q1.front());q1.pop();}Pop=q1.front();q1.pop();return Pop;}if(q1.empty()){while(q2.size()>1){q1.push(q2.front());q2.pop();}Pop=q2.front();q2.pop();return Pop;}return Pop;}int top() {if(!q1.empty())return q1.back();else if(!q2.empty())return q2.back();    return 0;   }bool empty() {return q1.empty() && q2.empty(); }
};

方案二
通过交换整个队列保证其中一个队列从表面上看,队头==栈顶,然后pop和top就很简单


class MyStack {
public:queue<int> q1;queue<int> q2;MyStack() {}void push(int x) {q2.push(x);while(!q1.empty()){q2.push(q1.front());q1.pop();}swap(q1,q2);//经过此次转化,q1队列队头就相当于栈顶}int pop() {int del=0;if(!q1.empty()){del=q1.front();q1.pop();}return del;}int top() {return q1.front();}bool empty() {return q1.empty() && q2.front();}
};

剑指 Offer 58 - I. 翻转单词顺序

【题目链接】
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。

示例 1:

输入: “the sky is blue” 输出: “blue is sky the” 示例 2:

输入: " hello world! " 输出: “world! hello” 解释:
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 示例 3:

输入: “a good example” 输出: “example good a” 解释:
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

解题思路:
把字符串按空格分割完毕存放到栈中,然后利用栈的特性完成

class Solution {
public:string reverseWords(string s) {stack<string> wordStack;  // 用栈存储单词string word = "";  // 存储当前单词string result = "";  // 存储结果// 遍历字符串for (int i = 0; i < s.size(); i++) {if (s[i] == ' ') {  // 遇到空格,把当前单词入栈if (word != "") {wordStack.push(word);word = "";}}else {  // 不是空格,把字符添加到当前单词word += s[i];}}// 把最后一个单词入栈if (word != "") {wordStack.push(word);}// 把单词出栈,拼接成结果字符串while (!wordStack.empty()) {result += wordStack.top();wordStack.pop();if (!wordStack.empty()) {result += " ";}}return result;}
};

剑指 Offer 59 - I. 滑动窗口的最大值

【题目链接】
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置最大值
[1 3 -1] -3 5 3 6 73
1 [3 -1 -3] 5 3 6 73
1 3 [-1 -3 5] 3 6 75
1 3 -1 [-3 5 3] 6 75
1 3 -1 -3 [5 3 6] 76
1 3 -1 -3 5 [3 6 7]7

蛮力法:超出时间限制

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {//蛮力法vector<int> v;int len=nums.size();for(int i=0;i<len-k+1;++i){int MinNUm=INT_MIN;for(int j=i;j<i+k;++j){MinNUm=MinNUm>nums[j]?MinNUm : nums[j];}v.push_back(MinNUm);}return v;}
};

优先级队列
利用堆的特性:大根堆堆顶最大。初始时,我们先将数组前 k 个元素放入优先队列中。每当我们向右移动窗口时,把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,如([4,2,3,2] 3)在这种情况下,初始化最大值是4,移动一次之后窗口里面没有4,但是4还在堆顶,我们要想办法去掉,由于这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其移除队列。

我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。我们可以使用pair存储一个键值对,数据:对应数组下标,当满足【滑动窗口最右边下标-堆顶元素对应的下标>=k】时,就可以删除堆顶元素

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {int n = nums.size();priority_queue<pair<int, int>> pq;//找到第一个滑动窗口最大值for (int i = 0; i < k; ++i) {pq.emplace(nums[i], i);}vector<int> v= {pq.top().first};//添加第一个最大值for (int i = k; i < n; ++i) {pq.emplace(nums[i], i);while ((pq.top().second+k) <= i ) {//如果栈顶元素不在滑动窗口中,删除,如[423]2 ;4[232]pq.pop();}v.push_back(pq.top().first);}return v;}
};

双端队列
把有可能成为滑动窗口最大值的数据存放进一个双端队列:当右滑遇到的元素比当前队列尾部元素大的时候,那么尾部元素就不可能成为最大值,删除!不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。
这就能保证队首对应的元素始终是滑动窗口最大值
特殊情况:当前处理的数据下标与最大值对应下标之差>=滑动窗口大小,这个数字已经不属于滑动窗口,直接删除这个最大值!这也导致了,队列中存放的不能是数组中的数据,而是数据对应的下标

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> vResult;//存储滑动窗口最大值if(nums.size()>=k && k>=1){deque<int>  dq_index;//存储滑动窗口值的下标for(int i=0;i<k;++i){//循环删除尾部比新插入小的元素while(!dq_index.empty() && nums[i]>nums[dq_index.back()])dq_index.pop_back();dq_index.push_back(i);    }vResult.push_back(nums[dq_index.front()]);for(int i=k;i<nums.size();++i){//循环删除尾部比新插入小的元素while(!dq_index.empty() && nums[i]>nums[dq_index.back()])dq_index.pop_back();dq_index.push_back(i);    //当前处理的数据下标与最大值对应下标之差>=滑动窗口大小,删除!if(!dq_index.empty() &&(i-dq_index.front())>=k){dq_index.pop_front();}    vResult.push_back(nums[dq_index.front()]);}}return vResult;}
};

剑指 Offer 30. 包含min函数的栈

【题目链接】
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

解题思路:
数据栈+辅助栈思想:数据栈负责存储元素,辅助栈存储当前栈中的最小元素值

class MinStack {
public:stack<int> s_data;stack<int> s_min;MinStack() {}void push(int x) {s_data.push(x);if(s_min.empty()||x<s_min.top())//如果min栈为空或者入栈的值小于min栈顶元素,{s_min.push(x);}else//入栈元素大于栈顶元素,最小值还是之前的元素{s_min.push(s_min.top());}}void pop() {if(s_data.empty() || s_min.empty())return;s_data.pop();s_min.pop();}int top() {assert(s_min.size()>0);return s_data.top();}int min() {assert(s_min.size()>0&& s_data.size()>0);return s_min.top();}
};

优化
可以发现辅助栈s_min每次都会push进一个元素,考虑进行小优化一下,当新插入的元素值大于当前min栈顶元素时不重复插入,只有当元素比栈顶小的时候在插入

class MinStack {
public:stack<int> s_data;stack<int> s_min;MinStack() {}void push(int x) {s_data.push(x);if(s_min.empty()||x<=s_min.top())//只需要插入新来的最小元素或相等元素{s_min.push(x);}}void pop() {if(s_data.empty() || s_min.empty())return;//分两种情况讨论删除    if(s_data.top()==s_min.top())s_min.pop();s_data.pop(); }int top() {assert(s_min.size()>0);return s_data.top();}int min() {assert(s_min.size()>0 && s_data.size()>0);return s_min.top();}
};

剑指 Offer 59 - II. 队列的最大值(max函数的队列)

【题目链接】
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:

输入: [“MaxQueue”,“pop_front”,“max_value”] [[],[],[]]
输出: [null,-1,-1]

解题思路:
借助辅助队列,从队列尾部插入元素时,删除队列中所有比这个元素小的元素,使得队列中只保留对结果有影响的数字

class MaxQueue {
public:MaxQueue() {}int max_value() {return dq.empty() ?-1:dq.front();}void push_back(int value) {//尾删比插入元素小的元素:对结果无影响while(!dq.empty() &&dq.back()<value){dq.pop_back();}dq.push_back(value);q.push(value);}int pop_front() {//删除数据并维护最大值队列if(q.empty()) return -1;int result=q.front();if(q.front()==dq.front()){dq.pop_front();}q.pop();return result;}
private:deque<int> dq;//存储每个阶段的最大值queue<int>  q;//存储所有数据       
};

包含min函数的栈和包含max函数的队列可以对比着看,深究之下可以发现它们都借助一个辅助的栈或队列实现


http://www.ppmy.cn/news/76419.html

相关文章

【数据库复习整理】数据库为什么要进行分库和分表以及水平分表和垂直分表

分库和分表原因 数据库进行分库和分表是为了解决大规模数据存储和高并发访问的需求&#xff0c;以提高系统的性能、可扩展性和可用性。以下是分库和分表的主要原因和好处&#xff1a; **分库的原因和好处&#xff1a;** 1. **数据隔离&#xff1a;** 分库将数据划分到不同的…

使用 CameraX 在 Jetpack Compose 中构建相机 Android 应用程序

使用 CameraX 在 Jetpack Compose 中构建相机 Android 应用程序 CameraX 是一个 Jetpack 库&#xff0c;旨在帮助简化相机应用程序的开发。 [camerax官方文档] https://developer.android.com/training/camerax CameraX的几个用例&#xff1a; Image CaptureVideo CapturePrev…

自动化早已不是那个自动化了,谈一谈自动化测试现状和自我感受……

前言 从2017年6月开始接触自动化至今&#xff0c;已经有好几年了&#xff0c;从17年接触UI自动化&#xff08;unittestselenium&#xff09;到18年接触接口自动化&#xff08;unittestrequests&#xff09;再到18年自己编写自动化平台&#xff08;后台使用python的flask&#…

day10 - 使用canny算子进行人像勾勒

本期主要介绍canny算子&#xff0c;了解canny算子的流程以及各个流程的原理和实现。 ​ 完成本期内容&#xff0c;你可以&#xff1a; 了解canny算子的流程和应用 若要运行案例代码&#xff0c;你需要有&#xff1a; 操作系统&#xff1a;Ubuntu 16 以上 或者 Windows10 工…

嘉兴桐乡考证培训-23年教资认定注意事项你知道吗?

又到了新的一年了&#xff0c;去年错过认定的同学们可以竖起耳朵啦~ 每年认定机会有两次&#xff0c;大部分省份一般上半年下半年各一次。 问&#xff1a;在校生可以认定么&#xff1f; 答&#xff1a;可以&#xff0c;但有年级限制&#xff1a;本科生大四最后一学期&#xf…

Linux多路转接之epoll

文章目录 一、select方案和poll方案还存在的缺陷二、epoll的认识1.epoll的基本认识2.epoll的原理3.epoll函数接口 三、编写epoll服务器四、epoll工作方式1.LT模式2.ET模式 一、select方案和poll方案还存在的缺陷 多路转接方案一开始是select方案&#xff0c;但是select方案缺点…

通过九点选择CRM系统

众所周知&#xff0c;CRM系统对于企业的发展至关重要。它可以帮助企业增强市场竞争力&#xff0c;拓展新的市场机会&#xff0c;提升品牌形象和口碑&#xff0c;提高客户满意度和忠诚度&#xff0c;实现业绩的大幅增长。那么选型时&#xff0c;CRM系统哪家好&#xff1f;看准这…

C++中的取余函数%、remainder、fmod以及matlab中的取余函数mod

C 1 整数取余 % 2 remainder函数 https://cplusplus.com/reference/cmath/remainder/?kwremainder double remainder (double numer , double denom); float remainder (float numer , float denom); long double remainder (long double numer, long double denom); doub…