代码随想录第12天 | 栈与队列part02 总结
题目:150. 逆波兰表达式求值
思路:
1.使用栈,存储数字,遇到运算符,则取出栈顶两个数进行运算,结果在存入栈中。
坑:
- 加减乘除运算符没有别的技巧,就是if相等 然后 +-*/ ,switch 也可以
- 栈使用long long型,int型会溢出
- 使用 "+"不是单引号'+',vector<string类型> 不是 vector<char类型>
- 编译错误,num1未定义, 作用域问题, if后没加{}
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long >stack;
for(auto & str:tokens){//注意引用&
if(str=="+"||str=="-"||str=="*"||str=="/") {//string类型是双引号""
long long num1=stack.top();
stack.pop();
long long num2=stack.top();
stack.pop();
long long sum=0;
if(str=="+") sum=num2+num1;
if(str=="-") sum=num2-num1;
if(str=="*") sum=num2*num1;
if(str=="/") sum=num2/num1;
stack.push(sum);
}else{
stack.push(stoll(str));//stoll()是string转long long 类型函数
}
}
return stack.top();
}
};
补充:
字符串类型转换
1.[数字] 转换 “字符串”
【函数名】
to_string(数字类型); //数字类型 int long float 等
2.“字符串” 转换 [数字]
【函数名】
stoi() 对应 int
stol() 对应 long
stoll() 对应 long long
题目:239. 滑动窗口最大值(二刷题)
思路:
- 暴力法,两层循环,一层遍历,一层遍历滑动窗口内,
时间复杂度:O(n^2) - 滑动窗口,左右删除,使用双端队列deque单调队列,将滑动窗口中的值存入队列,一直保持最大值在队首,新添加元素大于队首,则全部删除,反之添加在队首后
单调对列保存的是下标
时间复杂度:O(n)
坑:
笑鼠,看错三次题。下次好好理解题目
- 保持单调队列的递减性,以及使用下标判断,最大值是否在窗口内,
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int > result ;
deque<int> deque; //保存索引下标
//不通过, 单调栈内未递减
for(int i=0;i<nums.size();i++){
//从队尾加入新元素,新元素比队尾大,删除队尾,保持单调递减,队首为滑动窗口内最大值
while(!deque.empty()&&nums[deque.back()]<=nums[i]){
deque.pop_back();
}
// 添加元素
deque.push_back(i);
//队列保存的是下标,窗口在移动,所以判断队首是否要移除窗口
if(i-deque.front()>=k){
deque.pop_front();
}
if(i>=k-1){
result.push_back(nums[deque.front()]);
}
}
return result;
}
};
补充:
单调队列:保持单调递减或递增的原则的队列
双向队列deque
- 可以在队列的两端进行元素的插入和删除操作,deque是C++STL(标准模板库)中的一种容器,
- 包含#include
- 常用函数
push_back()//在队列的尾部插入元素。
emplace_front()//与push_front()的作用一样
push_front()//在队列的头部插入元素。
emplace_back()//与push_back()的作用一样
pop_back()//删除队列尾部的元素。
pop_front()//删除队列头部的元素。
back()//返回队列尾部元素的引用。
front()//返回队列头部元素的引用。
clear()//清空队列中的所有元素。
empty()//判断队列是否为空。
size()//返回队列中元素的个数。
begin()//返回头位置的迭代器
end()//返回尾+1位置的迭代器
rbegin()//返回逆头位置的迭代器
rend()//返回逆尾-1位置的迭代器
insert()//在指定位置插入元素
erase()//在指定位置删除元素
题目:347.前 K 个高频元素(二刷题)
思路:
- 使用map保存元素和出现频率,使用优先级队列进行排序(小顶堆),
坑:
- 优先级队列 container是容器类型,可以是vector,queue等用数组实现的容器,不能是list,默认可以用vector;
- 报错runtime error: reference binding to null pointer of type 'int' (stl_vector.h)
忽略了vector容器注意事项而报的错
原因:vector在还没有分配任何空间时还不能像数组一样用下标形式去访问vector的
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
//1.计算元素出现的频率
unordered_map<int,int > map;//key存元素,value存频率
for(auto &c: nums){
map[c]++;// 不用find查找,直接添加
}
//2. 使用优先级队列 小顶堆对频率排序,
//先自定义排序函数
struct cmp{
bool operator()(pair<int,int>&p1,pair<int,int>&p2){
return p1.second>p2.second;//小顶堆是较大元素下沉
}
}; //报错,忘加;
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> pri_q;
for(auto &a: map){
pri_q.push(a);
if(pri_q.size()>k)
pri_q.pop();
}
// 3.排序过后,转成vector<int>类型 return
//错误,因为是小顶堆,所以需要倒序存入vector
vector<int> result;
for(int i=k-1;i>=0;i--){
result.push_back(pri_q.top().first);
pri_q.pop();
}
return result;
}
};
补充:
优先级队列
一般队列是先进先出,从队尾入队,从队首出队。
优先级队列(priority_queue):给元素赋予优先级,优先级高的先出队。像数据结构 堆。
优先级队列的底层是最大堆或最小堆。大顶堆(堆头是最大元素,较小的元素下沉,less<),小顶堆(堆头是最小元素,较大的元素下沉greater>)
- 优先级队列的定义如下:
首先要包含头文件#include<queue>
priority_queue<typename, container, functional>
·typename是数据的类型;
·container是容器类型,可以是vector,queue等用数组实现的容器,不能是list,默认可以用vector;
·functional是比较的方式,默认大顶堆(元素越大,优先级越高),(c++可以直接使用自带的less和greater函数,默认的大顶堆是less函数,元素小于当前节点下沉)
使用自定义的数据类型的时候,可以重写比较函数,也可以进行运算符重载(less重载小于“<”运算符,构造大顶堆;greater重载大于“>”运算符,构造小顶堆)。
//重写仿函数,完成less的功能,也可以用class定义类,此时需要将运算符重载函数设为public
//结构体struct中默认是访问类型是public
struct cmp
{
bool operator() ( Data &a, Data &b) {
return a.getId() < b.getId();
}
};
- 优先队列具有队列的所有特性,包括队列的基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
和队列基本操作相同:
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 ( **并排序** )
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
栈与队列总结
- 栈与队列的特性和基本操作
- 底层实现
- 单调队列
- 优先级队列
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
今日总结
需补充堆的相关知识

浙公网安备 33010602011771号