1 题1——239. 滑动窗口最大值
一开始我的思路,推着推着发现有问题 fail!具体思路:首先,最左边的k个数的序列里面,找一个max,max_idx 和 次max,次max_idx,【维护4个值】。然后每次窗口右移时,与新的一个元素比较,考虑两个下标越界与不越界两种情况,更新或是不变这四个值。有些情况,max值可以由次max的值得到,但是次max的值有需要次次max的值(相应的idx随着变),次次max需要次^3的max,所以这个递归下去没完了。和打暴力没啥区别。
于是看 carl 的视频题解:
思路——因为当前窗口中max前面的数都没啥用,并且需要很快地(而不是再遍历一次)得到当前窗口的max值,所以每次都把max放在队列首部,max前面的都不在队列中 。而为了方便维护max放在队首,所以这个队列需要是单调递减的!因此,维护一个递减的且size是小于等于k(即,在当前窗口中的)的单调递减的队列!
注意,【1】单调队列 ≠ 优先级队列,单调队列就是队列中保持单调,而具体根据什么设计push、pop是因题而异【2】本题需要双端队列deque而不是queue,因为需要从尾部pop元素。
AC代码:
class Solution {
// carl思路: 维护一个递减的且size是小于等于k的单调递减的队列
public:
class my_que
{
deque<int> q; //双端队列
public:
void push(int x)
{
while(!q.empty() && x > q.back()) //x比尾巴还大 就不断地尾部排出元素【双端队列!】
q.pop_back();
q.push_back(x);
}
void pop(int x)
{
if(!q.empty() && x == q.front())q.pop_front();
}
int get_max()
{
return q.front(); //队列首部就是最大的值
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k)
{
my_que que;
vector<int> res;
// 第一个窗口
for(int i = 0; i < k;i++)
que.push(nums[i]);
res.push_back(que.get_max());
// 窗口移动
for(int i = k; i < nums.size();i++)
{
que.push(nums[i]);
que.pop(nums[i-k]);
res.push_back(que.get_max());
}
return res;
}
};
2 题2——347.前K个高频元素
思路:首先得到 key - 频率 的集合,之后用优先队列(分析得,需要小顶堆)自动排序,保持k个元素即可,最后传给输出vector。
思路容易想到,但是具体实现需要编程熟练度,比如一开始遍历数组存频率集合,要判断一个元素在不在集合中,且需要很快地拿到它,这时候用map!map插入元素的操作。还有小顶堆的定义,自定义排序的写法,map的一个元素塞入小顶堆时候的语法,优先队列的取第一个元素不用front而是用top等等语法知识。
为什么不用快排要用小顶堆呢? 因为,使用快排要将map转换为vector的结构,然后对整个数组进行排序, 而当前这种场景下,我们其实只需要维护 k 个有序的序列就可以了,所以使用优先级队列是最优的。
AC代码:
class Solution {
public:
class mycmp
{
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs)
{
return lhs.second > rhs.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k)
{
//setp1——算频率,由于需要有判断一个元素在不在一个集合中的操作 所以需要hash
// 由于需要存键值对所以map
// 由于不用有序所以unordered_map
unordered_map<int,int> map;
for(int i = 0; i < nums.size();i++)
{
// 插入元素的操作,过于冗余!!!!!
// auto iter = map.find(nums[i]);
// if(iter != map.end()) //找到了
// map[nums[i]]++;
// else
// map.insert({nums[i],1});
map[nums[i]]++; //【语法1】map插入元素的操作
}
//step2-
//priority_queue<pair<int,int>,vector<pair<int,int>>,greater<int>> que; //第二个参数装vector<第一个参数>就行 第3个参数错!
priority_queue<pair<int,int>,vector<pair<int,int>>,mycmp> que; //【语法2】 优先队列的定义且当排序规则需要自定义时多定义一个类作为第三个参数
// 定义一个小顶堆的优先队列
// pair的第一个元素是key 第二个元素是key对应的个数
for(auto u = map.begin(); u != map.end();u++)
{
que.push(*u); //【语法3】当要把map的一个键值对插入到队列中的操作 直接*u 不能map.first/second
if(que.size() > k)que.pop();
}
vector<int> res;
while(!que.empty())
{
res.push_back(que.top().first); //【语法4】注意!优先队列首部不写que.front() 写que.top()
que.pop();
}
return res;
}
};
总结:
1. 通过两题学习了一下单调队列与优先队列,第一题试了很久想不到,学到了单调队列!第二题基本能想到就是语法知识很多不熟!
2. 总共用时:5h+