目录
(六)逆波兰表达式求值
1. 题目描述
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
2. 思路
把数字压栈,遇到操作符就弹出两个数字进行计算并将结果压栈。
3. 解题过程
难易程度:中等
标签:栈、数组、数学
(1)错误代码
没有考虑到输入负数的情况,比如输入了 -11,按如下逻辑会进入 else 的判断,认为输入了操作符,所以应该检查是否为操作符。(否则需要单独判断是否为负数)
【std::stoll 函数用于将字符串转换为 long long。】
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> nums;
for(int i = 0; i < tokens.size(); i++){
if(tokens[i] >= "0" && tokens[i] <= "9"){
nums.push(stoll(tokens[i]));
}
else{
long long b = nums.top();
nums.pop();
long long a = nums.top();
nums.pop();
if(tokens[i] == "+") nums.push(a + b);
else if(tokens[i] == "-") nums.push(a - b);
else if(tokens[i] == "*") nums.push(a * b);
else if(tokens[i] == "/") nums.push(a / b);
}
}
int result = nums.top();
nums.pop();
return result;
}
};
(2)改正
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> nums;
for(int i = 0; i < tokens.size(); i++){
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/"){
long long b = nums.top();
nums.pop();
long long a = nums.top();
nums.pop();
if(tokens[i] == "+") nums.push(a + b);
else if(tokens[i] == "-") nums.push(a - b);
else if(tokens[i] == "*") nums.push(a * b);
else if(tokens[i] == "/") nums.push(a / b);
}
else{
nums.push(stoll(tokens[i]));
}
}
int result = nums.top();
nums.pop();
return result;
}
};
(七)滑动窗口最大值
1. 题目描述
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
2. 思路
单调队列,设计单调队列的时候,pop 和 push 操作要保持如下规则:
- pop(value):如果窗口移除的元素 value 等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果 push 的元素 value 大于入口元素的数值,那么就将队列入口的元素弹出,直到 push 元素的数值小于等于队列入口元素的数值为止
保持如上规则,每次窗口移动的时候,只要问 que.front() 就可以返回当前窗口的最大值。
C++的另一种思路:使用 multiset 作为单调队列
multiset 用以有序地存储元素的容器。允许存在相等的元素。在遍历原数组的时候,只需要把窗口的头元素加入到 multiset 中,然后把窗口的尾元素删除即可。因为 multiset 是有序的,并且提供了 *rbegin(),可以直接获取窗口最大值。
3. 解题过程
难易程度:困难
标签:队列、数组、滑动窗口、单调队列、堆(优先队列)
class Solution {
private:
class MyQueue {
private:
deque<int> que;
public:
// 如果push的数值大于队列尾元素的数值,就将队列后端元素弹出,直到push的数值小于等于队列入口元素的数值
void push(int x) {
while(!que.empty() && x > que.back()) {
que.pop_back();
}
que.push_back(x);
}
// 每次弹出的时候,比较当前要弹出的数值是否等于队列头元素的数值,如果相等则弹出。
void pop(int x) {
if(!que.empty() && x == que.front()) {
que.pop_front();
}
}
int front() {
return que.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> result;
// 第一个窗口,先全部调用push
for(int i = 0; i < k; i++) {
que.push(nums[i]);
}
result.push_back(que.front());
// 移动窗口
for(int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]); // 先把窗口滑动移出去的数字pop
que.push(nums[i]); // 把新加入的数字push
result.push_back(que.front());
}
return result;
}
};
(八)前 K 个高频元素
1. 题目描述
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
2. 思路
- 用 map 统计每个元素出现的频率
- 用优先队列对频率进行排序
3. 解题过程
难易程度:中等
标签:数组、哈希表、分治、桶排序、计数、快速选择、排序、堆(优先队列)
补充优先队列相关内容:
定义:priority_queue<Type, Container, Funcitonal>
Type 指数据类型, Container 指容器类型(必须是数组实现的容器,如 vector,deque,不能是list。STL 中默认是 vector),Functional 指比较的方式,当需要用自定义的类型时才需要传这三个参数,使用基本数据类型时,只需要传入数据类型即可,默认构造是大顶堆。
一般定义:【注意 greater<int> 后要加一个空格】
小顶堆 priority_queue<int, vector<int>, greater<int> > heap;
大顶堆 priority_queue<int> 或者 priority_queue<int, vector<int>, less<int> > heap;
Comparator接口说明:
返回负数,形参中第一个参数排在前面;返回正数,形参中第二个参数排在前面
对于队列:排在前面意味着往队头靠
对于堆(使用PriorityQueue实现):从队头到队尾按从小到大排就是最小堆(小顶堆), 从队头到队尾按从大到小排就是最大堆(大顶堆)
class Solution {
private:
// 小顶堆
class mycomparison {
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
return lhs.second > rhs.second;
}
};
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> count;
for(int i = 0; i < nums.size(); i++) {
count[nums[i]]++;
}
// 定义一个小顶堆,对频率排序
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
// 用固定大小为k的小顶堆,扫面所有频率的数值
for (auto it = count.begin(); it != count.end(); it++) {
pri_que.push(*it);
if (pri_que.size() > k) { // 堆的大小大于K,则弹出,保证堆的大小一直为k
pri_que.pop();
}
}
// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = pri_que.top().first;
pri_que.pop();
}
return result;
}
};
(九)总结
1. 栈的应用
简化路径、括号匹配、逆波兰表达式求值等。
2. 队列的应用
注意单调队列和优先队列的使用。