代码随想录day11--队列的应用

LeetCode239.滑动窗口最大值

题目描述:

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

返回 滑动窗口中的最大值 

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

解题思路:

·这道题能写出来完全是看题解写出来的,可以使用队列进行求解,放进窗口里的元素,随着窗口的移动,队列也一进一出。我们使用的队列一定要包含这三个函数,如下:

class MyQueue{
public:
    void pop(int value){//移除数值
    }
    void push(int value){//添加数值
    }
    int front(){
        return que.front();//获得队列头元素也就是最大值
    }
};

 *每当窗口移动的时候,调用que.pop,que.push(),然后que.front()就返回我们的最大值根据题目描述,我们知道最终结果中元素都是排序的,而且要最大值放在出对口,用于return

·说了那么多,还没说如何实现排完序后的队列,怎么样能把窗口要移除的元素弹出呢?其实不需要纠结,不需要在意所有的元素,只要找到可能成为窗口中最大值的元素即可(因为窗口在滑动),同时保证队列里的元素数值是由大到小的。这种特殊的队列就是单调队列(可递增也可递减)。C++中我们需要自己定义以及使用条件约束实现单调队列。

·在设计单调队列的时候,pop和push操作需要保持以下操作:

1.pop(value):如果窗口移除的元素value等于单调队列的出口氧元素,那么队列弹出元素,否则不用任何操作

2.push(value):如果push的元素value大于入口元素的数值,那么久将队列入口的元素弹出,知道push元素的数值小于等于队列入口元素的数值为止

按照这两个规则,每次移动窗口的时候,只用访问que.front()就可以返回当前窗口的最大值

*特别注意这道题中,需要使用deque实现单调队列,常用的queue在没有指定容器的情况下,deque就是默认底层容器。

代码如下:

class Solution {
private:
    class Myqueue{//实现单调队列
    public:
        deque que;
        void pop(int value){
            if(!que.empty() && que.front() == value){//pop之前判断队列当前是否为空
                                                     //若每次窗口弹出的数值等于队列出口元素的数组则弹出
                que.pop_front();
            }
        }
        void push(int value){//如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,知道push的数值小于等于队列入口元素的数值为止,这样就可以保持队列里的数值是单调的
            while(!que.empty() && value > que.back()){
                que.pop_back();
            }
            que.push_back(value);
        }
        int front(){
            return que.front();
        }
    };
public:
    vector maxSlidingWindow(vector& nums, int k) {
        Myqueue que;
        vector result;
        for(int i = 0;i < k;i++){//将前k个元素放入队列中
            que.push(nums[i]);
        }
        result.push_back(que.front());//使用result记录前k个元素的最大值
        for(int i = k;i < nums.size();i++){
            que.pop(nums[i-k]);//移除最前面元素
            que.push(nums[i]);//加入最后的元素
            result.push_back(que.front());//记录对应的最大值
        }
        return result;
    }
};

·时间复杂度:O(n)

·空间复杂度:O(k)

难点

·对单调队列的理解和实现

总结

有很多同学在求解的时候,会觉得难点是如何求一个区间的最大值。很多人会使用暴力求解,但是时间复杂度很高,说实话,在没看你题解之前,我也不知道可以使用队列进行求解,做题就是需要多见识不曾见过的题目类型或者解题方法。

LeetCode347.前k个高频元素

题目描述:

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

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

解题思路:

·这道题主要需要求解出以下三块内容:

1.统计元素出现频率

2.对频率排序

3.找出前K个高频元素

·首先按统计元素出现的频率,这一类的问题可以使用map来进行统计。然后对频率进行排序,这里需要使用优先级队列进行求解。优先级队列其实就是一个披着队列外衣的堆,因为优先级队列对外接口只从队头取元素,从队尾添加元素,再无其他取元素的方式。而且优先级队列内部元素是自动依照元素的权值排列。

·在缺省情况下,优先级队列利用大顶堆完成对元素的排序,这个大顶堆是以vector为表现形式的完全二叉树。

·在这道题中,我们使用优先级队列来对部分频率进行排序,使用小顶堆(父亲结点小于左右孩子),因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素怒

代码如下:

class Solution {
public:
    class mycomprison{//创建小顶堆
        public:
        bool operator()(const pair& lhs,const pair& rhs){
            return lhs.second > rhs.second;
        }
    };
    vector topKFrequent(vector& nums, int k) {
        unordered_map map;//map
        for(int i = 0;i < nums.size();i++){
            map[nums[i]]++;
        }

        //第一个参数表示队列中元素的类型,即由两个int类型组成的pair
        //第二个参数表示使用vector作为底层容器存储队列中的元素
        //第三个参数表示作为元素的比较方式
        priority_queue,vector>,mycomprison> pir_que;

        for(unordered_map::iterator it = map.begin();it != map.end();it++){//遍历小顶堆中的所有元素
            pir_que.push(*it);
            if(pir_que.size() > k){//如果堆的大小大于了k,则队列弹出,保证堆的大小一直为k
                pir_que.pop();
            }
        }

        vector result(k);
        for(int i = k-1;i >= 0 ;i--){//找出前K个高频元素,因为小顶堆先弹出的是最小的,所以使用倒序输出数组
            result[i] = pir_que.top().first;
            pir_que.pop();
        }
        return result;
    }
};

·时间复杂度:O(nlonk)

·空间复杂度:O(n)

难点:

·和上一题一样难的是对题目的理解

·对使用小顶堆的理解

·对优先级队列的理解

总结:要多做没见过的题目类型(虽然现在还没做多少),要将每一题套用合适的数据结构,这样才能简单、快速、高效的解决问题,如果没有看题解,这道题我就要使用暴力求解了,题解的思维确实特别巧妙。

你可能感兴趣的:(算法,数据结构,leetcode)