代码随想录刷题Day30

滑动窗口最大值

这道题需要使用的是优先队列/堆的数据结构,因为它是要不断地记录一串数据序列中的最大值,像这样的问题,堆可以自动维护堆顶元素是序列里的最大值/最小值。

这道题的思路是:

  1. 先把数组前k个数以pair<value,index>的形式放入堆中,把堆顶元素的value值放入答案向量res中;
  2. 对剩下的元素,依次比遍历
    1. 每次遍历时候,把当前元素的<value,index>放入堆中
    2. 从堆中取出堆顶元素,如果堆顶元素的index不在[i-k+1,i]这样一个范围内,就让堆pop掉堆顶元素,直到堆顶元素的index在[i-k+1,i]这个区间位置,并把堆顶元素的value值放入答案向量res中
    3. 不断循环上述两个步骤,直到所有元素遍历完
  3. 返回res结果

代码如下:

cpp 复制代码
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        int n = nums.size();
        priority_queue<pair<int,int>> a;    //默认先比较pair第一个元素,如果相等就比较下一个元素,第一个是value,第二个元素是Index.默认是大顶堆
        for(int i = 0;i<nums.size()&&i<k;i++){  //先把优先队列k个元素填充好
            a.emplace(nums[i],i);   //emplace函数可以用来创建一个优先队列的对象,并放入队列中
        }
        //第一个K序列里的最大的元素值
        res.push_back(a.top().first);

        //挨个遍历数组中的元素
        for(int i = k;i<n;i++){
            a.emplace(nums[i],i);
            while(a.top().second < i - k + 1){  //不断获取堆顶元素,直到堆顶元素的索引值在[i-k+1,i]范围内
                a.pop();
            }
            res.push_back(a.top().first);
        }
        return res;
        
    }
};

写代码发现,这里使用一个int n = nums.size() 比直接在循环遍历时候直接让**i<nums.size()**会少花不少时间。

前K个高频元素

这道题在有了上面这题的基础上,继续做的话,会很容易想到思路,这里也是使用优先队列的方式。

先对数组中的元素借助map进行次数的统计,接着使用优先队列的方式,对map中的<次数,值>重新按照次数的大小降序排列,最后从优先队列中pop出k个值,就可以得到出现次数为前k的元素值,代码如下:

cpp 复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        //使用map统计不同值出现的次数
        map<int,int> value_cnt_map;
        int n = nums.size();
        for(int i = 0;i<n;i++){
            if(value_cnt_map.find(nums[i])!= value_cnt_map.end()){
                value_cnt_map[nums[i]]++;
            }
            else{
                value_cnt_map[nums[i]] = 1;
            }
        }
        //使用优先队列对(次数,值)按照出现次数降序排列
        priority_queue<pair<int,int>> cnt_value_heap;
        for(map<int,int>::iterator it = value_cnt_map.begin();it!= value_cnt_map.end();++it){
            cnt_value_heap.emplace(it->second,it->first);
        }
        //k次提取堆顶的值,也就是出现次数前k大的值放入答案ans中
        vector<int> ans;
        while(k--){
            ans.push_back(cnt_value_heap.top().second);
            cnt_value_heap.pop();
        }
        return ans;
    }
};

栈与队列系列刷题小结

该系列刷题,我重新回顾了栈和队列的一些基础用法,以及STL中相关的数据结构的使用。

刷题主要有三类:

  • 栈和队列的基础性质:
    • 使用两个栈来实现队列,主要是如何用栈的LIFO(Last in,first out 后入先出)的性质去模拟队列的FIFO(First in,first out 先入先出),这里可以使用一个栈作为push站,一个栈作为pop/top的访问栈;
    • 使用两个队列来实现栈,对于栈的pop或者是top,就只能是从队列中挨个把元素取出来放到另一个队列中,直到取到队底的元素,就作为栈顶的元素弹出或者访问。
  • 栈系列的经典题目
    • 括号匹配
    • 删除字符串中的所有相邻重复项
    • 后缀表达式
    • 这些题目,应该算是经典而又基础的题了,只是要注意,在使用栈的时候,要注意空栈的情况,以及什么情况下栈中的元素要出栈,什么情况下要入栈,以及要注意出栈元素的顺序和实际入栈的顺序是相反的(这在表达式那道题有所体现)
  • 优先队列
    • 这个知识点,感觉更属于堆的知识,不过是会用到队列的一些基本操作,比如push,pop,top,emplace这些
    • 滑动窗口最大值
    • 前K个高频元素
    • 这些题,基本思路比较好想到,只是会在使用pair,或者map,以及emplace()这样的函数有些生疏。