第十三天|LeetCode:● 239. 滑动窗口最大值● 347.前 K 个高频元)

本文探讨了如何通过使用单调队列解决LeetCode中的239滑动窗口最大值问题,同时介绍了利用优先级队列处理347前K个高频元素的方法。通过对比不同数据结构的效率和适用场景,优化了时间复杂度并保持了元素顺序的稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:239. 滑动窗口最大值

题目:

1.暴力做法和为什么用双端队列

我们可以 双指针,一个指向第一个遍历,一个指向第三个向后遍历,每次遍历都for循环取出最大值,直到第二个快指针指向了最后一个就停止,这个时间复杂度为O(n*K),但是一般超过了10的9次方就会超时

由题意可得,超时了,所以我们可以用另一种方法,优先级队列(priority_queue)来写,虽然每次加了一个元素都会进行排序,但是队列只能删除第一个元素,用优先级队列顺序就会改变从而让不该先出去的元素出去了,所以不行,

有没有一种可以维护单调递减(单调递增)还能让删除顺序保持不变呢和删除的头就是最大值呢,我们,可以自己实现它,用双端队列成的单调队列。

2.单调队列解析

 图像:

                                                                                                                                                                                                                                                                                                                           

 2.c++代码

class Solution {
public:
    class MyQueue { //单调队列(从大到小)
    public:
    deque<int>que;
    void pop (int value) {
         if (value == que.front()) {
              que.pop_front();
         }
    }
    void push(int value) {
        while (!que.empty() && value > que.back()) {
            que.pop_back();
        }
        que.push_back(value);
    }
    int front() {
        return que.front();
    }

};
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {//单调队列
    MyQueue que;
    vector<int>result;

        int size = nums.size();
        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        result.push_back(que.front());
        for (int i = k; i < size; i++) {
             que.pop(nums[i - k]);
             que.push(nums[i]);
             result.push_back(que.front());
        } 
        return result;
    }
   
};

 4.思路

每次滑动窗口,取出最大值到结果集中,删除头元素,加入新元素到末尾,维护单调队列是因为每次我们加数据都会和前一个比较,如果比他打就删掉前面的,总让最大的再第一个

5.问题和代码解析:

1.push

    void push(int value) {

        while (!que.empty() && value > que.back()) {
            que.pop_back();
        }
        que.push_back(value);
    }
};

while中第一个条件是如果队列为空,说明才遍历到第一个元素,不需要删除,直接加入队列中,

while中第二个条件是如果前面有元素还有比前面的大(说明前面的元素必然不是最大值),就可以把前面的元素删掉,让自己当老大,push自己

2.pop

    void pop (int value) {
         if (value == que.front()) {
              que.pop_front();
         }
    }

这个条件是什么意思呢,就是如果        for (int i = k; i < size; i++)  遍历到的这个元素是最大值的话,就删掉他,

真正解释:因为push元素的时候把比最大值小的那些元素全部都删除了,所以顺序遍历时,会出现已经删掉的元素(匹配不到的数),所以我们不必要再次删除了, 直到匹配到的时候就说明这个数没删除,我们就可以删除了,因为是最大值,我们又需要把它加进结果集。

3.主函数

        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        result.push_back(que.front());
        for (int i = k; i < size; i++) {
             que.pop(nums[i - k]);
             que.push(nums[i]);
             result.push_back(que.front());
        } 

把前三个数先加入,不然没元素删,再删除第一个(如果没匹配就已经删除了不必要删),加入下一个,放进第一个(第一个是最大的)到结果集

这题的关键是他的元素减少了,我们可以用下标匹配的方法,防止误删,顺序不变 


题目链接:347. 前 K 个高频元素

 1.暴力做法和思考

我们可以把nums进行map存储,map[nums[i]]++,再对map进行排序取出前面n个元素

但我们不许要这样,我们只需要维持前面的k个元素最大就行了,到第k+1个元素删掉最小的

所以我们要排序,用到优先级队列。

2.c++代码

class Solution {
public:

class compare{
   public:
        bool operator()(const pair<int, int>& b, const pair<int, int>& a) {
            return b.second > a.second;
        }

};
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            map[nums[i]]++;
        }
        priority_queue<pair<int, int>, vector<pair<int, int>>, compare>pri_que;
        for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size() > k) {
                pri_que.pop();
            }
        }
    vector<int>result(k, 0);
    for (int i = k - 1; i >= 0; i--) {
        result[i] = pri_que.top().first;
        pri_que.pop();
    }
 return result;
    }
};

3.思路

先把数据存到map中,再定义一个优先级队列,遍历map,如果队列的元素个数大于k,就删掉一个,最后把优先级队列中的元素个数维持在k个元素,最后倒序输出就行了。

问题

用优先级队列先要定义一个比较函数

具体请看第一个栈与队列:

day10|LeetCode:● 232.用栈实现队列● 225. 用队列实现栈_星轨道交的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值