用两个栈实现队列
题目来源:牛客网
1、问题描述
描述
用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
数据范围: n\le1000n≤1000
要求:存储n个元素的空间复杂度为 O(n)O(n) ,插入与删除的时间复杂度都是 O(1)O(1)
2、思路解析
先将数据压入栈1,因为先进后出,所以出来的数据是反向的,所以又将栈1中数据压入栈2,出栈顺序为刚开始入栈的顺序。提前说明在压入栈2前要保证栈2为NULL,因为栈2不为NULL时就插入数据就会导致前边的数据进去的早,出来的完,就会导致顺序反向。
看一个例子:
可以看出栈2不为NULL时就插入数据,导致出栈顺序就会和入栈顺序不和,这就不符合队列的先进先出的性质
3、代码实现
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
//将栈中元素插入到栈2中
//直到栈2为NULL插入数据
if(stack2.empty()){
while(!stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}
}
int num=stack2.top();
stack2.pop();
return num;
}
private:
stack<int> stack1;
stack<int> stack2;
};
包含min函数的栈
题目来源:牛客网
1、问题描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。
此栈包含的方法有:
push(value):将value压入栈中
pop():弹出栈顶元素
top():获取栈顶元素
min():获取栈中最小元素
数据范围:操作数量满足 0 \le n \le 300 \ 0≤n≤300 ,输入的元素满足 |val| \le 10000 \ ∣val∣≤10000
进阶:栈的各个操作的时间复杂度是 O(1)\ O(1) ,空间复杂度是 O(n)\ O(n)
2、思路解析
底层还是stack来存储数据,但是最小数据使用的有序容器 set map都是有序容器,但是set不允许数据重复所以不能使用,那么map怎么做到的呢?将数据作为map的key,数据出现的次数作为values值当删除元素,只–出现的次数,当对应的次数到1时就删除数据,最小值就是map首元素的key值,直接返回就行。
3、代码实现
class Solution {
//栈+优先级队列
map<int,int> mp;
stack<int> s;
public:
void push(int value) {
s.push(value);
mp[value]++;
}
void pop() {
//删除栈顶元素同时删除hash中元素
if(mp[s.top()]==1){
//直接删除
mp.erase(s.top());
}else{
mp[s.top()]--;
}
s.pop();
}
int top() {
return s.top();
}
int min() {
return mp.begin()->first;
}
};
栈的压入、弹出序列
题目来源:牛客网
1、问题描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
- 0<=pushV.length == popV.length <=1000
- -1000<=pushV[i]<=1000
- pushV 的所有数字均不相同
2、思路解析
先将push数组中元素压入栈中直到栈顶元素等于popV的第一个元素,当栈顶元素不等于popV数组下一个元素就压入push数据的下一个元素
当遍历完push数组,就返回s.empty(),栈为NULL就表示这一堆数组就是入栈何出栈的顺序。
3、代码实现
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> s;
int index=0;
//先将数据压入栈中直到栈顶元素等于popV的第一个元素
for(int i=0;i<pushV.size();i++){
s.push(pushV[i]);
while(!s.empty()&&s.top()==popV[index]){
s.pop();
index++;
}
}
return s.empty();
}
};
滑动窗口的最大值
题目来源:牛客网
1、问题描述
描述
给定一个长度为 n 的数组 nums 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。
例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
数据范围: 1 \le size \le n \le 100001≤size≤n≤10000,数组中每个元素的值满足 |val| \le 10000∣val∣≤10000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
2、思路解析
滑动窗口+双端队列
利用双端队列记录当前滑动窗口的元素索引
队头记录滑动窗口中最大元素的索引
队列中存放的元素索引对应的是递减的,就是说队头元素就是区间内最大的元素,队列中元素索引对应元素是递减的
遍历数组:
- 如果队列最左侧索引已不在滑动窗口范围内,弹出队列最左侧索引
- 通过循环确保队列的最左侧索引所对应元素值最大
- 新元素入队
- 从第一个滑动窗口的末尾索引开始将最大值存储到结果v中
先形成大小为k的窗口
3、代码实现
class Solution {
public:
vector<int> maxInWindows(const vector<int>& nums, int size) {
vector<int> v;
deque<int> q;
int begin=0;
int end=1;
q.push_back(begin);
for(;end<=nums.size();end++){
//先取大小的size的窗口
if(end-begin+1>size){
//队头元素是窗口内最大元素
v.push_back(nums[q.front()]);
begin++;
}
//判断队头元素是不是在数据范围内------控制数据在窗口内部----在窗口内部寻找最大值
while(!q.empty()&&q.front()<begin){
q.pop_front();
}
//队尾元素小于新来元素就是直接删除-----控制队列中元素有序---将小于新来元素删除达到元素递增
while(!q.empty()&&nums[q.back()]<nums[end]){
q.pop_back();
}
//新来元素入对
q.push_back(end);
}
return v;
}
};