滑动窗口最大值
v1.0:直接双指针 固定滑动窗口 暴力超时
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
for(int left = 0, right = left + k - 1;right<nums.size();left++, right++)
{
int maxNum = nums[left];
for(int i = left;i<=right;i++)
{
if(nums[i]>maxNum)
{
maxNum = nums[i];
}
}
result.push_back(maxNum);
}
return result;
}
};
v2.0:使用定义的单调队列,单调队列需要用deque进行定义,因为需要从队列尾部取出元素,普通队列做不到这一点
在遍历时,先取出窗口外的元素,如果是队头元素则去除,这里如果不是队头元素就先留里面
如果是新元素与单调队列头进行比较,大于则清空队列,队列头改为新元素,小于则再比较是否大于单调队列尾
如果也不大于尾就加进来,大于尾就从尾部去掉小于新元素的值,再把新元素加进来
class Solution {
public:
struct MyQueue
{
public:
dequeuint> q;
void pop(int value)
{
if(!q.empty()&&value==q.front())
{
q.pop_front();
}
}
void push(int value)
{
while(!q.empty()&&value>q.back())
{
q.pop_back();
}
q.push_back(value);
}
int getMax()
{
return q.front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue q;
vector<int> result;
for(int i = 0; i<k;i++)
{
q.push(nums[i]);
}
result.push_back(q.getMax());
for(int i = k; i< nums.size();i++)
{
q.pop(nums[i-k]);
q.push(nums[i]);
result.push_back(q.getMax());
}
return result;
}
};
struct MyQueue {
private:
deque<int> q;
// 设计的核心思想:push进来的时候,从尾巴开始比,把小的都去掉。因为新来的后面有可能变成最大值
// 但是老的比新来的还小的就不可能变成最大值了
// pop的时候,如果是最大值,那就要pop,否则不用管
// 因为这个队列只需要返回最大值
public:
void Pop(int value)
{
if (!q.empty() && value == q.front())
{
q.pop_front();
}
}
void Push(int value)
{
while (!q.empty() && value > q.back())
{
q.pop_back();
}
q.push_back(value);
}
int getMax()
{
return q.front();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ret;
MyQueue q;
for (int i = 0; i < k; i++)
{
q.Push(nums[i]);
}
ret.emplace_back(q.getMax());
for (int i = k; i < nums.size(); i++)
{
q.Pop(nums[i - k]);
q.Push(nums[i]);
ret.emplace_back(q.getMax());
}
return ret;
}
前K个高频元素
思路
- 统计元素频率
- 对元素按照频率排名
- 找出前K个
统计元素频率
使用map即可
对元素按照频率排名
使用容器适配器:队列
因为要排名,使用优先级队列priority_queue
优先级队列是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。
优先级队列内部元素是自动依照元素的权值排列
缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)
堆
堆是完全二叉树,当树中每个节点都不小于其左右孩子时是“大顶堆”,当树中每个节点都不大于其左右孩子时是“小顶堆”,一个简单的具体实现就是使用priority_queue
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
使用快排VS使用优先级队列
如果不使用优先级队列而是使用快排,需要先将map映射为vector,再对整个数组进行快排
而使用优先级队列只需要维护k个有序的序列就可以了
为什么用的是小顶堆
如果使用大顶堆,队列的特点是只能从队头出,那样最大的元素就会被弹出,我们需要的就是最大的K个元素,所以应该使用小顶堆把最小的元素弹出
我的解法
v1.0:想先用排序来解决一下,在网上看到了如何把map转换为vector进行排序:创建一个以pair为元素的vector,再使用自定义的sort进行排序即可
class Solution {
public:
bool static cmp(const pair<int, int>& a, const pair<int, int>& b)
{
return a.second > b.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> umap;
for(auto num:nums)
{
umap[num]++;
}
vector<pair<int, int>> forSort(umap.begin(), umap.end());
sort(forSort.begin(), forSort.end(), cmp);
vector<int> result;
while(k--)
{
result.push_back(forSort[k].first);
}
return result;
}
};
v2.0:使用priority_queue
,这家伙居然要三个参数:元素类型、所适配的容器类型、重载运算符,详见
class Solution {
public:
bool static cmp(const pair<int, int> &a, const pair<int, int> &b)
{
return a.second > b.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> umap;
for(auto num:nums)
{
umap[num]++;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pri_que(cmp);
for(unordered_map<int, int>::iterator it = umap.begin(); it!=umap.end(); it++)
{
pri_que.push(*it);
if(pri_que.size()>k)
{
pri_que.pop();
}
}
vector<int> result;
while(k--)
{
result.push_back(pri_que.top().first);
pri_que.pop();
}
return result;
}
};
有个小疑问:
bool static cmp(const pair<int, int> &a, const pair<int, int> &b)
{
return a.second > b.second;
}
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> pri_que(cmp);
和
class Cmp{
public:
bool operator()(const pair<int, int> &a, const pair<int, int> &b)
{
return a.second>b.second;
}
};
priority_queue<pair<int, int>, vector<pair<int, int>>, Cmp> pri_que;
有什么区别?
第一个的话就是把函数指针作为参数传进pri_que
第二个是重载括号运算符,说明pri_que内部会这样调用Cmp(a,b);
bool static cmp(pair<int, int> p1, pair<int, int> p2)
{
return p1.second > p2.second;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> umap;
for (int i : nums)
{
umap[i]++;
}
vector<pair<int, int>> forSort(umap.begin(), umap.end());
sort(forSort.begin(), forSort.end(), cmp);
vector<int> ret;
for (int i = 0; i < k; i++)
{
ret.emplace_back(forSort[i].first);
}
return ret;
}
关键点
指定sort的对比方法时报错error:reference to non-static member function must be called
我们在力扣写代码时,是在solution类里面,我们直接在这里面写的函数是该类的成员函数,非静态成员函数有一个隐藏的隐式指针参数,指向调用它们的对象,例如前面的cmp函数
当我们尝试将非静态成员函数作为函数指针传递给**STL算法(如sort函数)**时,由于多一个this参数无法使用。因此我们需要将非静态成员函数转换为静态成员函数或全局函数,例如下面改成全局函数(力扣的solution类之外)
bool cmp(pair<int, int> p1, pair<int, int> p2)
{
return p1.second > p2.second;
}
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> umap;
for (int i : nums)
{
umap[i]++;
}
vector<pair<int, int>> forSort(umap.begin(), umap.end());
sort(forSort.begin(), forSort.end(), cmp);
vector<int> ret;
for (int i = 0; i < k; i++)
{
ret.emplace_back(forSort[i].first);
}
return ret;
}
};