代码随想录算法训练营第13天|● 239. 滑动窗口最大值● 347.前 K 个高频元素

本文介绍了滑动窗口问题的两种优化解法:一种是使用单调队列高效地找到每个窗口内的最大值,另一种是利用map和堆排序寻找前k个高频元素,特别强调了小根堆在本题中的优势。

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

239.滑动窗口最大值

解一(暴力解):假设窗口为k,数组大小为n,每次在一个窗口找最大值,遍历n-k次,则时间复杂度O(kn)

优解:时间消耗大的还是在找最大值方面,定义一个单调(从大到小单调减)队列,队列里面仅维护当前窗口可能的最大值。时间复杂度O(n)

单调队列遵循规则:

pop():队列非空,且并且当前value值等于单调队列的队头元素,则弹出队头元素

push(value):value值必须大于队尾元素,才把value加入单调队列,若不是,则一直弹出队尾元素直到队列为空或满足value大于队尾元素.再加入队列。

核心可以理解为:滑动窗口删除(pop)前一个元素,后加入(push)后一个元素可以实现滑动,因为队列仅保存当前窗口可能的最大值(并不是全保存,若当前队列中的值比新加入的元素--push进来的元素还小,则没必要再维护这些小值,因为迟早会被舍弃),数组前窗口删除的元素如果和队头元素相等就应该pop出来,保持队中元素和当前窗口元素的一致性。push则是把后窗口新加入的元素和队中元素比较,把队列中比新加入的元素还小的直接删除。以上就是对该解法比较核心的理解

代码:

class Solution {
    class MyQueue{//重新定义队列方法,单调递减
    public:
        deque<int> que;
        void pop(int value){
            if(!que.empty() && value==que.front()){//仅队列非空且队头元素和窗口最前面的移除值相同才pop
                que.pop_front();
            }
        }
        void push(int value){
            while(!que.empty() && value>que.back()){//仅队列非空且要push的元素比队尾元素大,则把队尾元素移除
                que.pop_back();
            }
            que.push_back(value);//此时push,前面的元素都不会比当前元素大
        }
        int front(){
            return que.front();
        }
    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        MyQueue que;//队列存放当前窗口可能的最大值
        //1,初始第一个窗口
        for(int i = 0;i < k;i++){//先push数组前k个元素
            que.push(nums[i]);
        }
        result.push_back(que.front());//再统计下当前窗口的最大值
        //2,窗口滑动
        for(int i = k;i < nums.size();i++){
            //i记录尾部位置,i-k记录头部位置
            que.pop(nums[i-k]);//滑动窗口移动前先删除最前面的元素
            que.push(nums[i]);//上面删除最前面的元素后,再加入后面的一个元素,实现滑动
            result.push_back(que.front());//记录当前窗口的最大值
        }
        return result;
    }
};

347.前k个高频元素

优解:可利用map记录该元素的出现次数,再对次数进行排序。时间差异主要出现在排序。要找到前k个高频出现的元素,推荐使用堆排序(又称优先级队列,仅有接口从队头取出元素,队尾插入元素),可以很方便的到结果,

但是对于该题第一时间会想到使用大根堆进行排序,若使用大根堆,当排序元素(出现次数)刚好为k,那么每次都将一个最大值弹出,需要排序弹出k次。并且还需要一个额外的空间去接收弹出的元素.相反使用小根堆,每次把最小的值弹出,最后剩k个元素即为结果,当排序元素仅为k时,就不需要排序直接输出,也不需要额外空间存储结果。

代码:(二刷再总结)

第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口算法的理解和应用能力。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值