代码随想录算法学习心得 11 | 239.滑动窗口的最大值、347.前k个高频元素...

一、滑动窗口的最大值

链接:力扣1

描述:

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。


思路:自定义一个队列,并且自己定义push和pop的规则。这个队列不用放窗口里的所有元素,只需要放可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。这种队列叫单调队列。

单调队列怎么配合窗口进行滑动呢?

设计单调队列的时候,pop,和push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止。

上述的push操作在排序的同时已经pop掉了不可能成为最大值的元素了,只需调用que.front()就可以返回当前窗口的最大值。 

queue在没有指定容器的情况下,deque就是默认底层容器,因此在自定义类使用双端队列来实现上述的规则。


代码如下:

#include <iostream>
#include <vector>
#include <deque>
using namespace std;
class Solution 
{
public:
    class Myqueue
    {
        deque<int>q;
    public:
        void pop(int val)
        {
            if (!q.empty() && q.front() == val)
            {
                q.pop_front();
            }
        }
        void push(int val)
        {
            while (!q.empty() && q.back() < val)
            {
                q.pop_back();
            }
            q.push_back(val);
        }
        int front()
        {
            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.front());
        for (int i = k; i < nums.size(); i++)
        {
            q.pop(nums[i - k]);
            q.push(nums[i]);
            result.push_back(q.front());
        }
        return result;
    }
};

int main()
{
    vector<int>v{ 1,3,-1,-3,5,3,6,7 };
    Solution s;
    auto ret= s.maxSlidingWindow(v, 3);
    return 0;
}

运行如下:


二、前K个高频元素

链接:力扣2

描述:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。


思路:先遍历数组,得到每个元素的频次。再对频率进行排序,可以使用一种 容器适配器就是优先级队列。priority_queue需要用到头文件queue。

优先级队列其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

而且优先级队列内部元素是自动依照元素的权值排列。排序的方法如下:

缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。

堆的补充知识:

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆(堆头是最大元素),小于等于左右孩子就是小顶堆(堆头是最小元素)。

选用大顶堆还是小顶堆?

对于一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,无法保留前k个高频元素,而且使用大顶堆就要把所有元素都进行排序。

如果用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。


代码实现如下:

#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
class Solution 
{
public:

    class Mycomp
    {
    public:
        bool operator()(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>m;
        for (int i = 0; i < nums.size(); i++)
        {
            auto mit = m.find(nums[i]);
            if (mit != m.end())
            {
                //找到了相同元素,值加1
                (*mit).second++;
            }
            else
            {
                m.insert(make_pair(nums[i], 1));
            }
        }
        priority_queue < pair<int, int>, vector<pair<int, int>>,Mycomp>pri_que;
        for (unordered_map<int, int>::iterator it = m.begin(); it != m.end(); it++)
        {
            pri_que.push(*it);
            if (pri_que.size() > k)
            {
                pri_que.pop();
            }
        }
        vector<int>result(k);//存放结果,跟result.resize(k);一样
      
        while (k--)
        {
            result.at(k) = pri_que.top().first;
            pri_que.pop();
        }
        return result;
    }
};
int main()
{
    Solution s;
    vector<int>v{ 1,1,1,2,3,3 };
    auto ret=s.topKFrequent(v, 3);
    return 0;
}

运行如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值